Tutorial: Develop an ASP.NET Core MVC web application with Azure Cosmos DB by using .NET SDK

This tutorial shows you how to use Azure Cosmos DB to store and access data from an ASP.NET MVC application that is hosted on Azure. In this tutorial, you use the .NET SDK V3. The following image shows the web page that you'll build by using the sample in this article:

Screenshot of the todo list MVC web application created by this tutorial - ASP NET Core MVC tutorial step by step

If you don't have time to complete the tutorial, you can download the complete sample project from GitHub.

This tutorial covers:

  • Creating an Azure Cosmos account
  • Creating an ASP.NET Core MVC app
  • Connecting the app to Azure Cosmos DB
  • Performing create, read, update, and delete (CRUD) operations on the data

Tip

This tutorial assumes that you have prior experience using ASP.NET Core MVC and Azure App Service. If you are new to ASP.NET Core or the prerequisite tools, we recommend you to download the complete sample project from GitHub, add the required NuGet packages, and run it. Once you build the project, you can review this article to gain insight on the code in the context of the project.

Prerequisites

Before following the instructions in this article, make sure that you have the following resources:

All the screenshots in this article are from Microsoft Visual Studio Community 2019. If you use a different version, your screens and options may not match entirely. The solution should work if you meet the prerequisites.

Step 1: Create an Azure Cosmos account

Let's start by creating an Azure Cosmos account. If you already have an Azure Cosmos DB SQL API account or if you're using the Azure Cosmos DB emulator, skip to Step 2: Create a new ASP.NET MVC application.

  1. Go to the Azure portal to create an Azure Cosmos DB account. Search for and select Azure Cosmos DB.

    The Azure portal Databases pane

  2. Select Add.

  3. On the Create Azure Cosmos DB Account page, enter the basic settings for the new Azure Cosmos account.

    Setting Value Description
    Subscription Subscription name Select the Azure subscription that you want to use for this Azure Cosmos account.
    Resource Group Resource group name Select a resource group, or select Create new, then enter a unique name for the new resource group.
    Account Name A unique name Enter a name to identify your Azure Cosmos account. Because documents.azure.com is appended to the ID that you provide to create your URI, use a unique ID.

    The ID can only contain lowercase letters, numbers, and the hyphen (-) character. It must be between 3-31 characters in length.
    API The type of account to create Select Core (SQL) to create a document database and query by using SQL syntax.

    The API determines the type of account to create. Azure Cosmos DB provides five APIs: Core (SQL) and MongoDB for document data, Gremlin for graph data, Azure Table, and Cassandra. Currently, you must create a separate account for each API.

    Learn more about the SQL API.
    Location The region closest to your users Select a geographic location to host your Azure Cosmos DB account. Use the location that is closest to your users to give them the fastest access to the data.

    The new account page for Azure Cosmos DB

  4. Select Review + create. You can skip the Network and Tags sections.

  5. Review the account settings, and then select Create. It takes a few minutes to create the account. Wait for the portal page to display Your deployment is complete.

    The Azure portal Notifications pane

  6. Select Go to resource to go to the Azure Cosmos DB account page.

    The Azure Cosmos DB account page

Go to the Azure Cosmos DB account page, and select Keys. Copy the values to use in the web application you create next.

Screenshot of the Azure portal with the Keys button highlighted on the Azure Cosmos DB account page

In the next section, you create a new ASP.NET Core MVC application.

Step 2: Create a new ASP.NET Core MVC application

  1. Open Visual Studio and select Create a new project.

  2. In Create a new project, find and select ASP.NET Core Web Application for C#. Select Next to continue.

    Create new ASP.NET Core web application project

  3. In Configure your new project, name the project todo and select Create.

  4. In Create a new ASP.NET Core Web Application, choose Web Application (Model-View-Controller). Select Create to continue.

    Visual Studio creates an empty MVC application.

  5. Select Debug > Start Debugging or F5 to run your ASP.NET application locally.

Step 3: Add Azure Cosmos DB NuGet package to the project

Now that we have most of the ASP.NET Core MVC framework code that we need for this solution, let's add the NuGet packages required to connect to Azure Cosmos DB.

  1. In Solution Explorer, right-click your project and select Manage NuGet Packages.

  2. In the NuGet Package Manager, search for and select Microsoft.Azure.Cosmos. Select Install.

    Install NuGet package

    Visual Studio downloads and installs the Azure Cosmos DB package and its dependencies.

    You can also use Package Manager Console to install the NuGet package. To do so, select Tools > NuGet Package Manager > Package Manager Console. At the prompt, type the following command:

    Install-Package Microsoft.Azure.Cosmos
    

Step 4: Set up the ASP.NET Core MVC application

Now let's add the models, the views, and the controllers to this MVC application.

Add a model

  1. In Solution Explorer, right-click the Models folder, select Add > Class.

  2. In Add New Item, name your new class Item.cs and select Add.

  3. Replace the contents of Item.cs class with the following code:

    namespace todo.Models
    {
        using Newtonsoft.Json;
    
        public class Item
        {
            [JsonProperty(PropertyName = "id")]
            public string Id { get; set; }
    
            [JsonProperty(PropertyName = "name")]
            public string Name { get; set; }
    
            [JsonProperty(PropertyName = "description")]
            public string Description { get; set; }
    
            [JsonProperty(PropertyName = "isComplete")]
            public bool Completed { get; set; }
        }
    }
    

Azure Cosmos DB uses JSON to move and store data. You can use the JsonProperty attribute to control how JSON serializes and deserializes objects. The Item class demonstrates the JsonProperty attribute. This code controls the format of the property name that goes into JSON. It also renames the .NET property Completed.

Add views

Next, let's create the following three views.

  • Add a list item view
  • Add a new item view
  • Add an edit item view

Add a list item view

  1. In Solution Explorer, right-click the Views folder and select Add > New Folder. Name the folder Item.

  2. Right-click the empty Item folder, then select Add > View.

  3. In Add MVC View, provide the following values:

    • In View name, enter Index.
    • In Template, select List.
    • In Model class, select Item (todo.Models).
    • Select Use a layout page and enter ~/Views/Shared/_Layout.cshtml.

    Screenshot showing the Add MVC View dialog box

  4. After you add these values, select Add and let Visual Studio create a new template view.

Once done, Visual Studio opens the cshtml file that it creates. You can close that file in Visual Studio. We'll come back to it later.

Add a new item view

Similar to how you created a view to list items, create a new view to create items by using the following steps:

  1. In Solution Explorer, right-click the Item folder again, select Add > View.

  2. In Add MVC View, make the following changes:

    • In View name, enter Create.
    • In Template, select Create.
    • In Model class, select Item (todo.Models).
    • Select Use a layout page and enter ~/Views/Shared/_Layout.cshtml.
    • Select Add.

Add an edit item view

And finally, add a view to edit an item with the following steps:

  1. From the Solution Explorer, right-click the Item folder again, select Add > View.

  2. In Add MVC View, make the following changes:

    • In the View name box, type Edit.
    • In the Template box, select Edit.
    • In the Model class box, select Item (todo.Models).
    • Select Use a layout page and enter ~/Views/Shared/_Layout.cshtml.
    • Select Add.

Once you complete these steps, close all the cshtml documents in Visual Studio as you return to these views later.

Add a controller

  1. In Solution Explorer, right-click the Controllers folder, select Add > Controller.

  2. In Add Scaffold, select MVC Controller - Empty and select Add.

    Select MVC Controller - Empty in Add Scaffold

  3. Name your new controller ItemController.

  4. Replace the contents of ItemController.cs with the following code:

    namespace todo.Controllers
    {
        using System;
        using System.Threading.Tasks;
        using todo.Services;
        using Microsoft.AspNetCore.Mvc;
        using Models;
    
        public class ItemController : Controller
        {
            private readonly ICosmosDbService _cosmosDbService;
            public ItemController(ICosmosDbService cosmosDbService)
            {
                _cosmosDbService = cosmosDbService;
            }
    
            [ActionName("Index")]
            public async Task<IActionResult> Index()
            {
                return View(await _cosmosDbService.GetItemsAsync("SELECT * FROM c"));
            }
    
            [ActionName("Create")]
            public IActionResult Create()
            {
                return View();
            }
    
            [HttpPost]
            [ActionName("Create")]
            [ValidateAntiForgeryToken]
            public async Task<ActionResult> CreateAsync([Bind("Id,Name,Description,Completed")] Item item)
            {
                if (ModelState.IsValid)
                {
                    item.Id = Guid.NewGuid().ToString();
                    await _cosmosDbService.AddItemAsync(item);
                    return RedirectToAction("Index");
                }
    
                return View(item);
            }
    
            [HttpPost]
            [ActionName("Edit")]
            [ValidateAntiForgeryToken]
            public async Task<ActionResult> EditAsync([Bind("Id,Name,Description,Completed")] Item item)
            {
                if (ModelState.IsValid)
                {
                    await _cosmosDbService.UpdateItemAsync(item.Id, item);
                    return RedirectToAction("Index");
                }
    
                return View(item);
            }
    
            [ActionName("Edit")]
            public async Task<ActionResult> EditAsync(string id)
            {
                if (id == null)
                {
                    return BadRequest();
                }
    
                Item item = await _cosmosDbService.GetItemAsync(id);
                if (item == null)
                {
                    return NotFound();
                }
    
                return View(item);
            }
    
            [ActionName("Delete")]
            public async Task<ActionResult> DeleteAsync(string id)
            {
                if (id == null)
                {
                    return BadRequest();
                }
    
                Item item = await _cosmosDbService.GetItemAsync(id);
                if (item == null)
                {
                    return NotFound();
                }
    
                return View(item);
            }
    
            [HttpPost]
            [ActionName("Delete")]
            [ValidateAntiForgeryToken]
            public async Task<ActionResult> DeleteConfirmedAsync([Bind("Id")] string id)
            {
                await _cosmosDbService.DeleteItemAsync(id);
                return RedirectToAction("Index");
            }
    
            [ActionName("Details")]
            public async Task<ActionResult> DetailsAsync(string id)
            {
                return View(await _cosmosDbService.GetItemAsync(id));
            }
        }
    }
    

The ValidateAntiForgeryToken attribute is used here to help protect this application against cross-site request forgery attacks. Your views should work with this anti-forgery token as well. For more information and examples, see Preventing Cross-Site Request Forgery (CSRF) Attacks in ASP.NET MVC Application. The source code provided on GitHub has the full implementation in place.

We also use the Bind attribute on the method parameter to help protect against over-posting attacks. For more information, see Tutorial: Implement CRUD Functionality with the Entity Framework in ASP.NET MVC.

Step 5: Connect to Azure Cosmos DB

Now that the standard MVC stuff is taken care of, let's turn to adding the code to connect to Azure Cosmos DB and do CRUD operations.

Perform CRUD operations on the data

First, we'll add a class that contains the logic to connect to and use Azure Cosmos DB. For this tutorial, we'll encapsulate this logic into a class called CosmosDBService and an interface called ICosmosDBService. This service does the CRUD operations. It also does read feed operations such as listing incomplete items, creating, editing, and deleting the items.

  1. In Solution Explorer, right-click your project and select Add > New Folder. Name the folder Services.

  2. Right-click the Services folder, select Add > Class. Name the new class CosmosDBService and select Add.

  3. Replace the contents of CosmosDBService.cs with the following code:

    namespace todo.Services
    {
        using System.Collections.Generic;
        using System.Linq;
        using System.Threading.Tasks;
        using todo.Models;
        using Microsoft.Azure.Cosmos;
        using Microsoft.Azure.Cosmos.Fluent;
        using Microsoft.Extensions.Configuration;
    
        public class CosmosDbService : ICosmosDbService
        {
            private Container _container;
    
            public CosmosDbService(
                CosmosClient dbClient,
                string databaseName,
                string containerName)
            {
                this._container = dbClient.GetContainer(databaseName, containerName);
            }
            
            public async Task AddItemAsync(Item item)
            {
                await this._container.CreateItemAsync<Item>(item, new PartitionKey(item.Id));
            }
    
            public async Task DeleteItemAsync(string id)
            {
                await this._container.DeleteItemAsync<Item>(id, new PartitionKey(id));
            }
    
            public async Task<Item> GetItemAsync(string id)
            {
                try
                {
                    ItemResponse<Item> response = await this._container.ReadItemAsync<Item>(id, new PartitionKey(id));
                    return response.Resource;
                }
                catch(CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
                { 
                    return null;
                }
    
            }
    
            public async Task<IEnumerable<Item>> GetItemsAsync(string queryString)
            {
                var query = this._container.GetItemQueryIterator<Item>(new QueryDefinition(queryString));
                List<Item> results = new List<Item>();
                while (query.HasMoreResults)
                {
                    var response = await query.ReadNextAsync();
                    
                    results.AddRange(response.ToList());
                }
    
                return results;
            }
    
            public async Task UpdateItemAsync(string id, Item item)
            {
                await this._container.UpsertItemAsync<Item>(item, new PartitionKey(id));
            }
        }
    }
    
  4. Repeat the previous two steps, but this time, use the name ICosmosDBService, and use the following code:

    namespace todo.Services
    {
        using System.Collections.Generic;
        using System.Threading.Tasks;
        using todo.Models;
    
        public interface ICosmosDbService
        {
            Task<IEnumerable<Item>> GetItemsAsync(string query);
            Task<Item> GetItemAsync(string id);
            Task AddItemAsync(Item item);
            Task UpdateItemAsync(string id, Item item);
            Task DeleteItemAsync(string id);
        }
    }
    
  5. In the ConfigureServices handler, add the following line:

    services.AddSingleton<ICosmosDbService>(InitializeCosmosClientInstanceAsync(Configuration.GetSection("CosmosDb")).GetAwaiter().GetResult());
    

    The code in the previous step receives a CosmosClient as part of the constructor. Following ASP.NET Core pipeline, we need to go to the project's Startup.cs file. The code in this step initializes the client based on the configuration as a singleton instance to be injected through Dependency injection in ASP.NET Core.

  6. Within the same file, add the following method InitializeCosmosClientInstanceAsync, which reads the configuration and initializes the client.

    /// <summary>
    /// Creates a Cosmos DB database and a container with the specified partition key. 
    /// </summary>
    /// <returns></returns>
    private static async Task<CosmosDbService> InitializeCosmosClientInstanceAsync(IConfigurationSection configurationSection)
    {
        string databaseName = configurationSection.GetSection("DatabaseName").Value;
        string containerName = configurationSection.GetSection("ContainerName").Value;
        string account = configurationSection.GetSection("Account").Value;
        string key = configurationSection.GetSection("Key").Value;
        CosmosClientBuilder clientBuilder = new CosmosClientBuilder(account, key);
        CosmosClient client = clientBuilder
                            .WithConnectionModeDirect()
                            .Build();
        CosmosDbService cosmosDbService = new CosmosDbService(client, databaseName, containerName);
        DatabaseResponse database = await client.CreateDatabaseIfNotExistsAsync(databaseName);
        await database.Database.CreateContainerIfNotExistsAsync(containerName, "/id");
    
        return cosmosDbService;
    }
    
  7. Define the configuration in the project's appsettings.json file. Open the file and add a section called CosmosDb:

      "CosmosDb": {
         "Account": "<enter the URI from the Keys blade of the Azure Portal>",
         "Key": "<enter the PRIMARY KEY, or the SECONDARY KEY, from the Keys blade of the Azure  Portal>",
         "DatabaseName": "Tasks",
         "ContainerName": "Items"
       }
    

If you run the application, ASP.NET Core's pipeline instantiates CosmosDbService and maintain a single instance as singleton. When ItemController processes client-side requests, it receives this single instance and can use it for CRUD operations.

If you build and run this project now, you should now see something that looks like this:

Screenshot of the todo list web application created by this database tutorial

Step 6: Run the application locally

To test the application on your local computer, use the following steps:

  1. Select F5 in Visual Studio to build the application in debug mode. It should build the application and launch a browser with the empty grid page we saw before:

    Screenshot of the todo list web application created by this tutorial

  2. Select the Create New link and add values to the Name and Description fields. Leave the Completed check box unselected. If you select it, the app adds the new item in a completed state. The item no longer appears on the initial list.

  3. Select Create. The app sends you back to the Index view, and your item appears in the list. You can add a few more items to your To-Do list.

    Screenshot of the Index view

  4. Select Edit next to an Item on the list. The app opens the Edit view where you can update any property of your object, including the Completed flag. If you select Completed and select Save, the app displays the Item as completed in the list.

    Screenshot of the Index view with the Completed box checked

  5. Verify the state of the data in the Azure Cosmos DB service using Cosmos Explorer or the Azure Cosmos DB Emulator's Data Explorer.

  6. Once you've tested the app, select Ctrl+F5 to stop debugging the app. You're ready to deploy!

Step 7: Deploy the application

Now that you have the complete application working correctly with Azure Cosmos DB we're going to deploy this web app to Azure App Service.

  1. To publish this application, right-click the project in Solution Explorer and select Publish.

  2. In Pick a publish target, select App Service.

  3. To use an existing App Service profile, choose Select Existing, then select Publish.

  4. In App Service, select a Subscription. Use the View filter to sort by resource group or resource type.

  5. Find your profile, and then select OK. Next search the required Azure App Service and select OK.

    App Service dialog box in Visual Studio

Another option is to create a new profile:

  1. As in the previous procedure, right-click the project in Solution Explorer and select Publish.

  2. In Pick a publish target, select App Service.

  3. In Pick a publish target, select Create New and select Publish.

  4. In App Service, enter your Web App name and the appropriate subscription, resource group, and hosting plan, then select Create.

    Create App Service dialog box in Visual Studio

In a few seconds, Visual Studio publishes your web application and launches a browser where you can see your project running in Azure!

Next steps

In this tutorial, you've learned how to build an ASP.NET Core MVC web application. Your application can access data stored in Azure Cosmos DB. You can now continue with these resources: