Create A .NET 5 App To Perform CRUD Operations On Azure Cosmos DB (SQL API) Using EF Core

Introduction

In this article, we will learn how to create a .NET 5 API that can do CRUD operations on Azure Cosmos DB using EF Core.

Prerequisites 

To follow along please create a database in Azure Cosmos DB called VideogamesDB and a container called Videogames.

Step 1 

  1. Open VS 2019 and create a new ASP .NET Core Web API.
  2. Choose the target framework as .NET 5.
  3. Install the NuGet package "Microsoft.EntityFrameworkCore.Cosmos" version 5.0.1

Step 2 - Edit appsettings.json file

We have to edit the appsettings.json file to include the Account, Primary Key, and Database name for Azure Cosmos DB. Copy-paste the following code into the appsettings.json file

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "CosmosDb": {
    "Account": "<>",
    "Key": "<>",
    "DatabaseName": "<>",
    "ContainerName": "<>"
  },
  "AllowedHosts": "*"
}

Replace "Account" value with URI and "Key" with Primary Key from Azure Cosmos DB (refer to screenshots below)

Create a .NET 5 App to perform CRUD operations on Azure Cosmos DB (SQL API) using EF Core

Replace DatabaseName and ContainerName with Database and ID from the Overview section of the Azure Cosmos DB(refer to the screenshot below)

Create a .NET 5 App to perform CRUD operations on Azure Cosmos DB (SQL API) using EF Core

Step 3 - Create DB Context

DbContext is used to create a session with database and will be used to perform all CRUD operations. 

Create a new folder called DBContext and add a new class ApplicationDbContext inside it. Copy-paste the following code into ApplicationDbContext

using AzureCosmosEFCoreCRUD.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

namespace AzureCosmosEFCoreCRUD.DBContext
{
    public class ApplicationDbContext : DbContext
    {
        public ApplicationDbContext(DbContextOptions dbContextOptions) : base(dbContextOptions)
        {
        }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            
            modelBuilder.Entity<Videogames>()
                 .HasNoDiscriminator()
                 .ToContainer(nameof(Videogames))
                 .HasPartitionKey(da => da.Id)
                 .HasKey(da => new { da.Id });
            modelBuilder.ApplyConfiguration(new VideogameEntityConfiguration());
           
        }
        public DbSet<Videogames> Videogames { get; set; }
    }
    public class VideogameEntityConfiguration : IEntityTypeConfiguration<Videogames>
    {
        public void Configure(EntityTypeBuilder<Videogames> builder)
        {
           builder.OwnsOne(x => x.Company);
        }
    }
    
}

Step 4 - Add Model classes

Add folder called Models. Add two model classes - Company and Videogame. Copy-paste the following code into the classes

namespace AzureCosmosEFCoreCRUD.Models
{
    public class Company
    {
        public string Name { get; set; }
        public string City { get; set; }
        public string Province { get; set; }
        public string Country { get; set; }
    }
}
using Newtonsoft.Json;
using System;
using System.ComponentModel.DataAnnotations;

namespace AzureCosmosEFCoreCRUD.Models
{
    public class Videogames
    {
        [Key]
        [JsonProperty(PropertyName = "id")]
        public string Id { get; set; } = Guid.NewGuid().ToString();
        public string Name { get; set; }
        public string Genere { get; set; }
        public string Platform { get; set; }
        public Company Company { get; set; }
    }
}

Step 5 - Edit Startup class

In the ConfigureServices method we have to add the following lines of code to configure Cosmos DB with ApplicationDbContext

var accountEndpoint = Configuration.GetValue<string>("CosmosDb:Account");
var accountKey = Configuration.GetValue<string>("CosmosDb:Key");
var dbName = Configuration.GetValue<string>("CosmosDb:DatabaseName");
services.AddDbContext<ApplicationDbContext>(x => x.UseCosmos(accountEndpoint, accountKey, dbName));

Step 6 - Add Controller

In the Controllers folder add a new Controller class called VideogameController. Copy-paste the following code into class.

These are simple endpoints to show how we can easily use LINQ style queries to perform CRUD operations on the database using DbContext. 

using AzureCosmosEFCoreCRUD.DBContext;
using AzureCosmosEFCoreCRUD.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace AzureCosmosEFCoreCRUD.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class VideogameController : ControllerBase
    {
        private readonly ILogger _logger;
        private readonly ApplicationDbContext applicationDbContext;
        private readonly IConfiguration configuration;

        public VideogameController(ApplicationDbContext applicationDbContext, IConfiguration configuration, ILogger<VideogameController> logger)
        {
            this.applicationDbContext = applicationDbContext;
            this.configuration = configuration;
            _logger = logger;
        }
        [HttpGet("GetAllVideogames")]
        public async Task<ActionResult<IList<Videogames>>> GetAllVideogames()
        {
            try
            {
                var videogames = await applicationDbContext.Videogames.ToListAsync();
                if (videogames != null)
                {
                    return Ok(videogames);
                }
                return NotFound("No results found");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex.Message);
                return StatusCode(500, "Some error occured while retrieving data");
            }
        }
        [HttpGet]
        public async Task<ActionResult<IList<Videogames>>> GetVideogamesByName(string name)
        {
            try
            {
                var videogames = await applicationDbContext.Videogames.FirstOrDefaultAsync(x => x.Name == name);
                if (videogames != null)
                {
                    return Ok(videogames);
                }
                return NotFound("No results found for "+name);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex.Message);
                return StatusCode(500, "Some error occured while retrieving data");
            }
        }
        [HttpGet("GetVideogameByCompanyName")]
        public async Task<ActionResult<IList<Videogames>>> GetVideogameByCompanyName(string name)
        {
            try
            {
                var videogames = await applicationDbContext.Videogames.Where(x => x.Company.Name == name).ToListAsync();
                if (videogames != null)
                {
                    return Ok(videogames);
                }
                return NotFound("No results found for " + name);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex.Message);
                return StatusCode(500, "Some error occured while retrieving data");
            }
        }
        [HttpPost]
        public async Task<ActionResult> AddVideogame(Videogames videogame)
        {
            try
            {
                await applicationDbContext.Videogames.AddAsync(videogame);
                await applicationDbContext.SaveChangesAsync();
                return Ok();
            }
            catch(Exception ex)
            {
                _logger.LogError(ex.Message);
                return StatusCode(500, "Some error occured while adding data");
            }
        }
        [HttpDelete]
        public async Task<ActionResult> DeleteVideogameByName(string name)
        {
            try
            {
                var videogame = await applicationDbContext.Videogames.FirstOrDefaultAsync(x => x.Name == name);
                if (videogame != null)
                {
                    applicationDbContext.Remove(videogame);
                    await applicationDbContext.SaveChangesAsync();
                    return Ok();
                }
                return NotFound();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex.Message);
                return StatusCode(500, "Some error occured while deleting data");
            }
        }
        [HttpPut]
        public async Task<ActionResult> UpdateVideogameByName(Videogames data)
        {
            try
            {
                var videogame = await applicationDbContext.Videogames.FirstOrDefaultAsync(x => x.Name == data.Name);
                if (videogame != null)
                {
                    var itemBody = applicationDbContext.Videogames.WithPartitionKey(videogame.Id);
                    itemBody.FirstOrDefault().Platform = "XBOX";
                    applicationDbContext.Update(itemBody.FirstOrDefault());
                    await applicationDbContext.SaveChangesAsync();
                    return Ok();
                }
                return NotFound();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex.Message);
                return StatusCode(500, "Some error occured while updating data");
            }
        }
    }
}

Summary

This is a very basic article to serve as a base for how to use EF Core to do CRUD operations on Azure Cosmos DB using .NET 5 REST API

Source Code: https://github.com/tanujgyan/AzureCosmosEFCoreCRUD