We talked in previous articles about the pluggability of OData with any storage technology regardless of its schema, whether it’s a SQL-based storage, NoSQL, In-Memory or simply a file on a hard-drive.
This power of OData enables developers to work with powerful, planet-scale storage technologies such as Cosmos DB.
In this article we are going to deep dive into one of three ways you can integrate Cosmos DB with OData but before we start, let’s talk a little bit about Cosmos DB, it’s capabilities and why it’s important to be able to expose that storage powerful through an API with OData.
What is Cosmos DB?
Azure Cosmos DB is Microsoft’s globally distributed, multi-model database service. It enables you to elastically and independently scale throughput and storage across any number of Azure regions worldwide. You can elastically scale throughput and storage, and take advantage of fast, single-digit-millisecond data access using your favorite API including SQL, MongoDB, Cassandra, Tables, or Gremlin.
Cosmos DB is a perfect solution for the hot category of storage, specifically data that needs to be accessed frequently and be retrieved as fast as possible.
Cosmos DB with OData
When planning to integrate Cosmos DB with OData, there are three different ways you can follow to accomplish that level of integration based on your preference and whichever model fits the application you’re developing.
If you don’t have any specific preference when it comes to integrating Cosmos DB with OData, I highly recommend you try them all, and judge for yourself which approach is easier and best fits your needs.
Let’s talk about the first of these approaches in this article.
First Approach: Pre-Built ASP.NET Core Solution
Cosmos DB in Azure Portal comes with some amazing options that enables you to download a full solution with full CRUD operations and UI to interact with Cosmos DB.
In order for you to do that, go to your Azure portal and create a new Cosmos DB resource as follows:
- Click on “Create a resource” button at the top left corner in your Azure Portal:
- Search for Azure Cosmos DB, then click “Create” button.
- When you click the “Create” button, you will be presented with a bunch of options to select which API you’d like to choose to interact with Cosmos DB – for the first and second approach we are going to select “Core (SQL)” as our option as follows:
You might need to create a new resource group and select an account name – for this demo I have named my account studentsdb.
Make sure your account name is all lowercase.
After that’s done, click “Review + create” button to create your new Cosmos DB instance.
The creation process of a Cosmos DB instance might take between 1 – 5 minutes depends on what type of configuration you’ve chosen to build your instance.
- When the creation is completed – go to the quick start option on the left side menu, you will be presented with the following options to work with Cosmos DB.
- Select “.NET Core” option then click the “Create ‘Items’ Contains” button – it will create a sample container for you to work with.
- Once the items container is created, click the “Download” button to download a full ASP.NET Core MVC solution for full CRUD operations to interact with the sample container you just created.
- Now that you have downloaded the solution, build the solution, then try to run the solution to make sure all the configurations are set correctly, all dependencies are downloaded and that you can actually interact with the container in your Cosmos DB instance – in this demo I’m going to add a couple of records in the ToDoList.
(Note: you might be prompted by Visual Studio 2019 that you’re opening a solution downloaded from the internet – since Azure portal is a trusted source go ahead and click OK)
To verify the records you’ve created have been successfully persisted, you can go back to your Azure portal and click “Open Data Explorer” button to check the records you created, the following screenshots shows you the data navigation and retrieval on the data explorer window in Azure:
This completes and verifies the setup portion of this first approach.
Now, let’s go ahead an utilize the CRUD operations code to build a new API and integrate OData.
You will notice that the solution we downloaded from Azure Portal implements the Repository pattern, which offer all CRUD operations needed to run an API.
For this demo, we will focus on the following code in ItemController.cs file:
private readonly IDocumentDBRepository<todo.Models.Item> Respository; public ItemController(IDocumentDBRepository<todo.Models.Item> Respository) { this.Respository = Respository; } [ActionName("Index")] public async Task<IActionResult> Index() { var items = await Respository.GetItemsAsync(d => !d.Completed); return View(items); }
We will create an API controller let’s call it ItemsController.cs and use the exact same code snippet above to build an endpoint that returns all items from Cosmos DB in JSON format – our new controller would look something like this:
using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using todo; using todo.Models; namespace quickstartcore.Controllers { [Produces("application/json")] [Route("api/Items")] public class ItemsController : Controller { private readonly IDocumentDBRepository<Item> Respository; public ItemsController(IDocumentDBRepository<Item> Respository) { this.Respository = Respository; } // GET: api/Items [HttpGet] public async Task<IEnumerable<Item>> Get() { return await Respository.GetItemsAsync(d => !d.Completed); } } }
Let’s verify our endpoint is functional by hitting the endpoint in the browser or using a tool like Postman, your response for hitting an endpoint:
http://localhost:1234/api/items
Your response should look something like this:
[ { "id": "3f0beb77-dd24-4b51-a921-6a34c60e7b4b", "name": "Brush your teeth", "description": "brush your teeth tonight", "isComplete": false }, { "id": "739c3cef-58b6-48a6-ad8f-21b485bb326f", "name": "Organize Office", "description": "Organize your office", "isComplete": false } ]
Now that we have implemented and verified our controller endpoint is functional, let’s do the exact same steps we’ve done in this article to implement OData in our solution, I will summarize these steps here as follows:
- Add a Nuget package Microsoft.AspNetCore.OData to your solution.
- Add OData service in your startup.cs file, then enable dependency injection along with the functions you want OData to provide through your API, here’s an example of how your file should look like:
using System.Linq; using Microsoft.AspNet.OData.Extensions; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using todo; namespace quickstartcore { public class Startup { public Startup(IHostingEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) .AddEnvironmentVariables(); Configuration = builder.Build(); } public IConfigurationRoot Configuration { get; } public void ConfigureServices(IServiceCollection services) { // Add framework services. services.AddMvc(); services.AddSingleton<IDocumentDBRepository<todo.Models.Item>>(new DocumentDBRepository<todo.Models.Item>()); services.AddOData(); } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseBrowserLink(); } else { app.UseExceptionHandler("/Home/Error"); } app.UseStaticFiles(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Item}/{action=Index}/{id?}"); routes.EnableDependencyInjection(); routes.Select().Filter().OrderBy().Expand(); }); } } }
- Let’s update our controller (ItemsController.cs) to enable OData query – your controller file should look as follows:
using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNet.OData; using Microsoft.AspNetCore.Mvc; using todo; using todo.Models; namespace quickstartcore.Controllers { [Produces("application/json")] [Route("api/Items")] public class ItemsController : Controller { private readonly IDocumentDBRepository<Item> Respository; public ItemsController(IDocumentDBRepository<Item> Respository) { this.Respository = Respository; } // GET: api/Items [HttpGet] [EnableQuery()] public async Task<IEnumerable<Item>> Get() { return await Respository.GetItemsAsync(d => !d.Completed); } } }
- Let’s verify our new endpoint with OData integration by hitting an endpoint like:
http://localhost:36442/api/items?$select=name
And your response should look as follows:[ { "name": "Brush your teeth" }, { "name": "Organize Office" } ]
By seeing this result, we verified OData is functional on your API retrieving and processing data from your Cosmos DB instance container.
In the next article, we will discuss working with OData & Cosmos DB using the EntityFramework in ASP.NET Core API.
Final Notes
- You can download the solution I used to run this demo from here (Don’t forget to update the endpoint & key values in DocumentDBRepository.cs file.
- The current demo uses ASP.NET Core 2.2 example, but there are so many other options including classic ASP.NET MVC applications that I encourage you to try out.
- The current demo uses Item as a model, you will need to modify the solution slightly to work with whatever model you want to run your application.
- I highly recommend following this link to learn more about Cosmos DB and it’s capabilities, huge thanks to Cosmos DB team for providing such a great documentation around such powerful technology.
- I also highly recommend staying up-to-date with all the new updates in Cosmos DB by following this link to their blog.
The post Integrating Cosmos DB with OData (Part 1) appeared first on OData.