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 will 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
  • Perform 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, ensure that you have the following resources:

All the screenshots in this article have been taken using Microsoft Visual Studio Community 2019. If your system is configured with a different version, it is possible that your screens and options may not match entirely, but if you meet the above prerequisites this solution should work.

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 are using the Azure Cosmos DB emulator for this tutorial, you can skip to Create a new ASP.NET MVC application section.

  1. Sign in to the Azure portal.

  2. Select Create a resource > Databases > Azure Cosmos DB.

    The Azure portal Databases pane

  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 Enter 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 Core (SQL) 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.

    Select Core (SQL) to create a document database and query by using SQL syntax.

    Learn more about the SQL API.
    Location Select 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

Now navigate to the Azure Cosmos DB account page, and click Keys, as these values are used 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. In Visual Studio, from the File menu, choose New, and then select Project. The New Project dialog box appears.

  2. In the New Project window, use the Search for templates input box to search for "Web", and then select ASP.NET Core Web Application.

    Create new ASP.NET Core web application project

  3. In the Name box, type the name of the project. This tutorial uses the name "todo". If you choose to use something other than this name, then wherever this tutorial talks about the todo namespace, adjust the provided code samples to use whatever you named your application.

  4. Select Browse to navigate to the folder where you would like to create the project. Select Create.

  5. The Create a new ASP.NET Core Web Application dialog box appears. In the templates list, select Web Application (Model-View-Controller).

  6. Select Create and let Visual Studio do the scaffolding around the empty ASP.NET Core MVC template.

  7. Once Visual Studio has finished creating the boilerplate MVC application, you have an empty ASP.NET application that you can run 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. The Azure Cosmos DB .NET SDK is packaged and distributed as a NuGet package. To get the NuGet package in Visual Studio, use the NuGet package manager in Visual Studio by right-clicking on the project in Solution Explorer and then select Manage NuGet Packages.

    Screenshot of the right-click options for the web application project in Solution Explorer, with Manage NuGet Packages highlighted.

  2. The Manage NuGet Packages dialog box appears. In the NuGet Browse box, type Microsoft.Azure.Cosmos. From the results, install the Microsoft.Azure.Cosmos package. It downloads and installs the Azure Cosmos DB package and its dependencies. Select I Accept in the License Acceptance window to complete the installation.

    Alternatively, you can use the Package Manager Console to install the NuGet package. To do so, on the Tools menu, select NuGet Package Manager, and then select Package Manager Console. At the prompt, type the following command:

    Install-Package Microsoft.Azure.Cosmos
    
  3. After the package is installed, your Visual Studio project should contain the library reference to 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. From the Solution Explorer, right-click the Models folder, select Add, and then select Class. The Add New Item dialog box appears.

  2. Name your new class Item.cs and select Add.

  3. Next replace the code in "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; }
        }
    }
    

    The data stored in Azure Cosmos DB is passed over the wire and stored as JSON. To control the way your objects are serialized/deserialized by JSON.NET, you can use the JsonProperty attribute as demonstrated in the Item class you created. Not only can you control the format of the property name that goes into JSON, you can also rename your .NET properties like you did with the Completed property.

Add views

Next, let's create the following three views:

Add a list item view

  1. In Solution Explorer, expand the Views folder, right-click the empty Item folder that Visual Studio created for you when you added the ItemController earlier, click Add, and then click View.

    Screenshot of Solution Explorer showing the Item folder that Visual Studio created with the Add View commands highlighted

  2. In the Add View dialog box, update the following values:

    • In the View name box, type Index.
    • In the Template box, select List.
    • In the Model class box, select Item (todo.Models).
    • In the layout page box, type ~/Views/Shared/_Layout.cshtml.

    Screenshot showing the Add View dialog box

  3. After you add these values, select Add and let Visual Studio create a new template view. Once done, it opens the cshtml file that is created. You can close that file in Visual Studio as you will 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. From the Solution Explorer, right-click the Item folder again, select Add, and then select View.

  2. In the Add View dialog box, update the following values:

    • In the View name box, type Create.
    • In the Template box, select Create.
    • In the Model class box, select Item (todo.Models).
    • In the layout page box, type ~/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, and then select View.

  2. In the Add View dialog box, do the following:

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

Once this is done, close all the cshtml documents in Visual Studio as you return to these views later.

Add a controller

  1. From the Solution Explorer, right-click the Controllers folder, select Add, and then select Controller. The Add Scaffold dialog box appears.

  2. Select MVC Controller - Empty and select Add.

    Screenshot of the Add Scaffold dialog box with the MVC Controller - Empty option highlighted

  3. Name your new controller, ItemController, and replace the code in that file 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. There is more to it than just adding this attribute, your views should work with this anti-forgery token as well. For more on the subject, and examples of how to implement this correctly, see Preventing Cross-Site Request Forgery. 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 details, please see Basic CRUD Operations 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 perform CRUD operations.

Perform CRUD operations on the data

The first thing to do here is 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 performs the CRUD and read feed operations such as listing incomplete items, creating, editing, and deleting the items.

  1. From Solution Explorer, create a new folder under your project named Services.

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

  3. Add the following code to the CosmosDBService class and replace the code in that file 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 steps 2-3, but this time, for a class named ICosmosDBService, and add 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. The previous code receives a CosmosClient as part of the constructor. Following ASP.NET Core pipeline, we need to go to the project's Startup.cs and initialize the client based on the configuration as a Singleton instance to be injected through Dependency Injection. In the ConfigureServices handler, we define:

    services.AddSingleton<ICosmosDbService>(InitializeCosmosClientInstanceAsync(Configuration.GetSection("CosmosDb")).GetAwaiter().GetResult());
    
  6. Within the same file, we define our helper method InitializeCosmosClientInstanceAsync, which will read the configuration and initialize 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. The configuration is defined in the project's appsettings.json file. Open it 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"
       }
    

Now if you run the application, ASP.NET Core's pipeline will instantiate CosmosDbService and maintain a single instance as Singleton; when ItemController is used to process client side requests, it will receive this single instance and be able to use it to perform 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 machine, use the following steps:

  1. Press 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. Click the Create New link and add values to the Name and Description fields. Leave the Completed check box unselected otherwise the new item is added in a completed state and doesn't appear on the initial list.

  3. Click Create and you are redirected back to the Index view and your item appears in the list. You can add a few more items to your todo list.

    Screenshot of the Index view

  4. Click Edit next to an Item on the list and you are taken to the Edit view where you can update any property of your object, including the Completed flag. If you mark the Complete flag and click Save, the Item will be displayed as completed in the list.

    Screenshot of the Index view with the Completed box checked

  5. You can verify at any point 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, press 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 on the project in Solution Explorer and select Publish.

  2. In the Publish dialog box, select App Service, then select Create New to create an App Service profile, or choose Select Existing to use an existing profile.

  3. If you have an existing Azure App Service profile, select a Subscription from the dropdown. Use the View filter to sort by resource group or resource type. Next search the required Azure App Service and select OK.

    App Service dialog box in Visual Studio

  4. To create a new Azure App Service profile, click Create New in the Publish dialog box. In the Create App Service dialog, enter your Web App name and the appropriate subscription, resource group, and App Service 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 that can access data stored in Azure Cosmos DB. You can now proceed to the next article: