Search code examples
c#entity-framework-coreazure-cosmosdb

Entity Framework create Cosmos partition like SQL table


I'm accustomed to working with EF and SQL (or similar RDBs) and am trying to learn to work with EF and Cosmos for a project.

I'm at the point of wanting to save records, which complains "Resource Not Found", which makes sense, the partition doesn't exist. So I am connected to the DB, EF is configured correctly (I believe) as it connects and attempts to save to the Cosmos DB, but it cannot find the partition.

How do I create the partition here?

In SQL world, I would:

  1. Create a database
  2. Create a model class
  3. Create a db context class
  4. Create and run an ef migration, which would construct the tables, analogous to the Cosmos containers.

So far in Cosmos world, I have:

  1. Created a database (in the local emulator)
  2. Created my model class
  3. Created a db context, and defined an entity (see below)

I have no idea how to tell it to create the context. I've tried the standard Add-Migration Initial, but it complains

Unable to resolve service for type 'Microsoft.EntityFrameworkCore.Migrations.IMigrator'. This is often because no database provider has been configured for this DbContext. A provider can be configured by overriding the 'DbContext.OnConfiguring' method or by using 'AddDbContext' on the application service provider. If 'AddDbContext' is used, then also ensure that your DbContext type accepts a DbContextOptions object in its constructor and passes it to the base constructor for DbContext.

The DB service is configured in the program file and the connection is working when I run the app, see the code below.

I feel I'm missing something obvious, but the tutorials I've found all create the partition manually first, which seems to defy the EF code-first pattern. I would rather avoid manually creating/managing every partition in every environment for Cosmos, as I don't manually create or manage tables when using EF and SQL - or am I completely off-base, and automatic partition creation just isn't a thing?

CosmosContext.cs:

using System.ComponentModel.DataAnnotations.Schema;
using System.Configuration;
using Microsoft.EntityFrameworkCore;

namespace MyTestProject;

public class CosmosContext : DbContext
{
    public CosmosContext(
        DbContextOptions<CosmosContext> options
        ) : base(options)
    {
    }

    public DbSet<CarModel> Cars { get; set; }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        builder.Entity<CarModel>()
            .ToContainer(nameof(Cars))
            .HasNoDiscriminator()
            .HasKey("Id");        
    }
}

Program.cs:

builder.Services.AddDbContext<CosmosContext>(
    options =>
    {
        options.UseCosmos(
            "https://localhost:8081",
            builder.Configuration["Cosmos:DevKey"],
            databaseName: builder.Configuration["Cosmos:DbName"]
        );
        options.EnableSensitiveDataLogging();
    });

Also tried:

  • Ensuring all relevant packages are installed and up to date
  • Added another context to this application, connected to a local SQL instance, which is working as expected. The setup is exactly as above except for SQL.

Solution

  • CosmosDB SQL is totally different animal compared to ANSI-SQL and relational DBs.

    • In CosmosDB you don't create or manage partitions explicitly, except just declare your partition key on database creation. After that, the "partitioning scheme" is locked and can no longer be changed, but it just works automatically.
    • there are no migrations. CosmosDB is schemaless and Add-Migration etc are not usable. If you want to migrate document models then it is purely data operation and it's up for your app to decide when-how to do this. See also Schema Migration Scripts in NoSQL Databases.

    If cosmosContext.Database.EnsureCreated() was the fix then most likely your "resource not found"-error was caused by Database not being created yet.

    You are good to go and just write docs to partitions as you are. No worrying required.