Highlight

Storing data is key aspect of every application out there. Learn how to use Azure Cosmos DB with .NET Core to deliver CRUD operation capabilities to application of any kind, and how to handle those operations in real time on front-end using Vue.js.

What is Cosmos DB and why should anyone use it?

Cosmos DB is one of available Azure PaaS (Platform as a Service) databases. It delivers low latency and high scalability data storage anywhere in the world. Cosmos DB is simply said a document database which stores its data in JSON format. It also has several APIs allowing for easy integration with many languages and frameworks. Those APIs include

Biggest advantage of using databases like Cosmos DB is that they will scale better than databases but also offer great ‘pay as you go’ model which means for small applications the cost is much lower than the one of full-fledged database. Of’course this doesn’t mean classic relational database should not be used, on the contrary, in some cases they are very much recommended.

Creating Cosmos DB

Creation of Cosmos DB is very easy. Few clicks in Azure Portal and developer is good to go. It is recommended that for production usage environment setup should be automated using either Azure CLI or PowerShell with use of ARM templates.

Log into Azure Portal Click on plus sign on the left-hand side corner, type in cosmos is search box and then press enter From available list of services click on Azure Cosmos DB and press Create button Fill in the service details

ID - name of the service, it will be available as public DNS and therefore must be globally unique

- name of the service, it will be available as public DNS and therefore must be globally unique API - a type of connectivity for Cosmos DB, currently only one API can be enabled at the time

- a type of connectivity for Cosmos DB, currently only one API can be enabled at the time Resource Group - logical container name where the resource will be created

- logical container name where the resource will be created Location - datacenter region location

- datacenter region location Enable Geo Redundancy - setting which will replicate database across multiple datacenter regions

Create new collection by navigating to Data Explorer and clicking on New Collection button Provide collection details and hit OK

Database Id - logical container name for collections

- logical container name for collections Collection Id - name for the container

- name for the container Storage Capacity - how much data can the collection hold, leave it at 10GB

- how much data can the collection hold, leave it at 10GB Throughput - How many Request Units can collection process per second

- How many Request Units can collection process per second Unique Keys - similarly to relational database this is ‘unique’ key on the column (value). It can be also put on nested values like /Address/Id. Note that it not required to add unique key on ‘id’ column as this is enabled by default.

Summarizing

In this section

A new Azure Cosmos DB called marczakio-vuejs-crud was created

was created A new database with Id Demo was created

was created A new collection with Id Todo was created

Building a Web Application

Next step is to create a web app backbone that will allow for creation of new to-do items and a web api that will handle the request.

This section will cover all 4 (four) basic actions for to-do’s which are create, read, update and delete.

Start with the web app template

This post is a follow-up to previous post Building .NET Core 2.0 web apps with Vue.js single file components using VS Code. A following template uses techniques explained in that article.

Clone simple vuejs template by opening Command Line and executing following commands from working directory git clone https://github.com/MarczakIO/netcore-vuejs-template.git cd netcore-vuejs-template dotnet restore npm install code . Open .gitignore file and add following line appsettings.local.json Now add appsettings.local.json file inside of root directory for the project. This file will contain connection string and credentials to the database. In previous step it was added to gitignore so that it won’t be committed to the repo. { "DatabaseSettings" : { "DatabaseId" : "<created_database_id>" , "Endpoint" : "<cosmos_db_endpoint>" , "AuthKey" : "<cosmos_db_auth_key>" } } Navigate to Keys blade on azure portal Copy value of URI and paste it into Endpoint value and Primary Key into AuthKey inside of appsettings.local.json file DatabaseId is the name of the database created earlier. In case of this article the name was “Demo” Create folder called Models in root directory Add model class DatabaseSettings.cs inside of Models folder which will hold specified configuration values using System ; namespace MarczakIO.VueSPA.Web.Models { public class DatabaseSettings { public string DatabaseId { get ; set ; } public string Endpoint { get ; set ; } public string AuthKey { get ; set ; } } } Now modify Startup.cs class so that it loads the options from appsettings.local.json file into a service whenever called for using dependency injection. using System ; using System.Collections.Generic ; using System.Linq ; using System.Threading.Tasks ; using Microsoft.AspNetCore.Builder ; using Microsoft.AspNetCore.Hosting ; using Microsoft.Extensions.Configuration ; using Microsoft.Extensions.DependencyInjection ; using Microsoft.AspNetCore.SpaServices.Webpack ; using System.IO ; using Microsoft.AspNetCore.Http ; using MarczakIO.VueSPA.Web.Models ; namespace MarczakIO.VueSPA.Web { public class Startup { public Startup ( IHostingEnvironment env ) { var builder = new ConfigurationBuilder () . SetBasePath ( env . ContentRootPath ) . AddJsonFile ( "appsettings.json" , optional : false , reloadOnChange : true ) . AddJsonFile ( "appsettings.local.json" , optional : true ) . AddEnvironmentVariables (); Configuration = builder . Build (); } public IConfiguration Configuration { get ; } public void ConfigureServices ( IServiceCollection services ) { services . Configure < DatabaseSettings >( options => Configuration . GetSection ( "DatabaseSettings" ). Bind ( options )); services . AddMvc (); } public void Configure ( IApplicationBuilder app , IHostingEnvironment env ) { if ( env . IsDevelopment ()) { app . UseDeveloperExceptionPage (); app . UseWebpackDevMiddleware ( new WebpackDevMiddlewareOptions { HotModuleReplacement = true }); } app . UseDefaultFiles (); app . UseStaticFiles (); app . UseMvc ( routes => { routes . MapRoute ( name : "default" , template : "{controller}/{action}/{id?}" ); }); app . Run ( async ( context ) => { context . Response . ContentType = "text/html" ; await context . Response . SendFileAsync ( Path . Combine ( env . WebRootPath , "index.html" )); }); } } } DatabaseOptions with Configure method every time IOptions is called in any constructor in the application a dependency injection container will make sure to initialize that property. Additionally because a configuration object has been given appsetings.local.json as additional config file then each time dependency injection initializes object all properties will be automatically populated with values from json file

Reading documents

Order of which actions developer builds first is not of high importance although usually it’s best to start with create or read actions. In this article read action will be built first as it’s possible to create document objects in azure portal. It will also make it much easier to work with other methods when it’s possible to see results immediately.

Build a Web API for document reading

Add nuget package for CosmosDB by using following command from command line dotnet add package Microsoft.Azure.DocumentDB.Core dotnet restore Note: Sometimes it is required to restart Visual Studio Code to pick up new packages or it will report namespacing issues. Create folder called Azure in root directory Add new class file called DocumentDBRepository.cs using System ; using MarczakIO.VueSPA.Web.Models ; using Microsoft.Extensions.Options ; using Microsoft.Azure.Documents ; using Microsoft.Azure.Documents.Client ; using System.Threading.Tasks ; using System.Collections.Generic ; using Microsoft.Azure.Documents.Linq ; using System.Linq.Expressions ; using System.Linq ; namespace MarczakIO.VueSPA.Web.Azure { public class DocumentDBRepository < T > where T : class { private DatabaseSettings dbOptions ; private DocumentClient client ; public DocumentDBRepository ( IOptions < DatabaseSettings > _dbOptions ) { dbOptions = _dbOptions . Value ; client = new DocumentClient ( new Uri ( dbOptions . Endpoint ), dbOptions . AuthKey ); } public async Task < IEnumerable < T >> GetItemsAsync ( string CollectionId , Expression < Func < T , bool >> predicate ) { IDocumentQuery < T > query = client . CreateDocumentQuery < T >( UriFactory . CreateDocumentCollectionUri ( dbOptions . DatabaseId , CollectionId ), new FeedOptions { MaxItemCount = - 1 , EnableCrossPartitionQuery = true }) . Where ( predicate ) . AsDocumentQuery (); List < T > results = new List < T >(); while ( query . HasMoreResults ) { results . AddRange ( await query . ExecuteNextAsync < T >()); } return results ; } } } Initialize depdendency injection for DocumentDBRepository inside of Startup.cs class file in ConfigureServices method public void ConfigureServices ( IServiceCollection services ) { services . Configure < DatabaseSettings >( options => Configuration . GetSection ( "DatabaseSettings" ). Bind ( options )); services . AddSingleton ( typeof ( DocumentDBRepository <>), typeof ( DocumentDBRepository <>)); services . AddMvc (); } Create folder called Controllers in root directory Now it’s just a controller for Web API that is missing, so add it inside of Controllers folder called TodoController.cs using System ; using System.Collections.Generic ; using System.Linq ; using System.Threading.Tasks ; using MarczakIO.VueSPA.Web.Azure ; using Microsoft.AspNetCore.Mvc ; using Microsoft.Azure.Documents ; using Newtonsoft.Json ; namespace MarczakIO.VueSPA.Web.Controllers { [Route("api/[controller] ")] public class TodoController : Controller { private DocumentDBRepository < Todo > todoRepo ; private string CollectionId = "Todo" ; public TodoController ( DocumentDBRepository < Todo > _todoRepo ) { todoRepo = _todoRepo ; } [HttpPost("[action] ")] public async Task < Todo > GetAll ([ FromBody ] Todo todo ) { return await todoRepo . CreateItemAsync ( CollectionId , todo ); } public class Todo { [JsonProperty("id")] public string Id { get ; set ; } [JsonProperty("name")] public string Name { get ; set ; } } } }

Build front-end for document creation