DEV Community

Kenichiro Nakamura
Kenichiro Nakamura

Posted on

C# - EF 5 with Cosmos DB: Multiple Entity Types in single Collection

The biggest difference between SQL Server and Cosmos DB, at least for me, is the ability to store different entity/document type in single collection. (of course depending on scenario)

In Distinguishing between different document types article, it gives us following sample.

Book documents:
{
    "id": "b1",
    "name": "Azure Cosmos DB 101",
    "bookId": "b1",
    "type": "book"
}

Review documents:
{
    "id": "r1",
    "content": "This book is awesome",
    "bookId": "b1",
    "type": "review"
},
{
    "id": "r2",
    "content": "Best book ever!",
    "bookId": "b1",
    "type": "review"
}
Enter fullscreen mode Exit fullscreen mode

Both document has:

  • id as unique key
  • bookId as good partition candidate
  • type to distinguish entity/document type/kind

In this article, I query these document by using EF 5.

Prerequisites

  • Cosmos DB Account
  • Basic C# knowledge

Create Console App

Let's see how we can query different entity type from single collection by using console app.

1. Create console app with .NET 5 and add Microsoft.EntityFrameworkCore.Cosmos NuGet package. I added version 5.0.5.

2. Add Models folder and add following models. I changed Review to have ReviewId and let Cosmos DB create Id.

public class Book
{
    public string Name { get; set; }
    public string BookId { get; set; }
    public string Type = nameof(Book);
}

public class Review
{
    public string ReviewId { get; set; }
    public string Content { get; set; }
    public string BookId { get; set; }
    public string Type = nameof(Review);
}
Enter fullscreen mode Exit fullscreen mode

3. Add CosmosContext.cs and paste following code.

  • It inherits from DbContext
  • In constructor, it delete existing database and create new one
  • It setup two DbSet. Books and Reviews
  • In OnConfiguring method, it reads connection string
public class CosmosContext : DbContext
{
    public CosmosContext()
        : base()
    {
        Database.EnsureDeleted();
        Database.EnsureCreated();
    }

    public DbSet<Book> Books { get; set; }
    public DbSet<Review> Reviews { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseCosmos("<your cosmos db connection string>");
    }
}
Enter fullscreen mode Exit fullscreen mode

4. Add following method in CosmosContext.cs. Here I build model. I use ToContainer to specify container name as name is different from default naming conventions. (books and reviews)
I also setup Partition Key, Discriminator and Key.

The Discriminator is the key to distinguish entity type.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Book>()
        .ToContainer("booksandreviews")
        .HasPartitionKey(x => x.BookId)
        .HasDiscriminator<string>(nameof(Book.Type));

    modelBuilder.Entity<Review>()
        .ToContainer("booksandreviews")
        .HasPartitionKey(x => x.BookId)
        .HasDiscriminator<string>(nameof(Review.Type));
    modelBuilder.Entity<Review>().HasKey(x => x.ReviewId);
}
Enter fullscreen mode Exit fullscreen mode

5. In Program.cs main method, add following code. I simply add three documents and query them by using DbSet.

static async Task Main(string[] args)
{
    var context = new CosmosContext();

    context.Add(new Book
    {
        Name = "Azure Cosmos DB 101",
        BookId = "b1"
    });

    context.Add(new Review
    {
        Content = "This book is awesome",
        BookId = "b1",
        ReviewId = "r1"
    });
    context.Add(new Review
    {
        Content = "Best book ever!",
        BookId = "b1",
        ReviewId = "r2"
    });

    await context.SaveChangesAsync();
    var books = await context.Books.ToListAsync();
    var reviews = await context.Reviews.ToListAsync();

    JsonConvert.DefaultSettings = () => new JsonSerializerSettings
    {
        Formatting = Formatting.Indented,
    };

    Console.WriteLine(JsonConvert.SerializeObject(books));
    Console.WriteLine(JsonConvert.SerializeObject(reviews));
}
Enter fullscreen mode Exit fullscreen mode

6. Run and see the result.

image

7. Query in Cosmos DB Explorer as well.

image

That's it! It's easy enough to use Entity Framework to query Cosmos DB (SQL API) even if I use multiple entity types in single collection :)

Reference

EF Core Azure Cosmos DB Provider

Top comments (2)

Collapse
 
mattferderer profile image
Matt Ferderer

You might want to consider not using BookId as your partition key.

learn.microsoft.com/en-us/azure/co...

There is a video on that page that explains as well. Ideally you want something that is a constant value that doesn't change. You also want something that if you divided all your documents into separate stacks based on your partition key, you would have roughly equal stack sizes.

Collapse
 
kenakamu profile image
Kenichiro Nakamura

You are absolutely right. We need to carefully think about partition key strategy! Thanks for pointing out!