C# - EF 5 with Cosmos DB: Multiple Entity Types in single Collection
Tags: entityframework, cosmosdb, csharp, sdk
Published: 2021-04-17 06:25:33

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](https://docs.microsoft.com/en-us/azure/cosmos-db/modeling-data#distinguishing-between-different-document-types) article, it gives us following sample.

```json
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"
}
```

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**.

```csharp
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);
}
```
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

```csharp
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>");
    }
}
```
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.

```csharp
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);
}
```

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

```csharp
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));
}
```

6\. Run and see the result.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kzybn4tr3w9p6uauv992.png)
 
7\. Query in Cosmos DB Explorer as well.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pj0jo44ff7paw27ro9m2.png)
 
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](https://docs.microsoft.com/en-us/ef/core/providers/cosmos/?tabs=dotnet-core-cli)