Configure EF Core with Cosmos DB to store ICollection of string
Asked Answered
K

4

7

Entity framework has some nice documentation about Embedding entities but I cannot figure out how to embed a simple string array IEnumerable<string>.

Sample class

public class Post {
  public string Id {get;set;}
  public string Content {get;set;}
  public IEnumerable<string> Tags {get;set;}
}

This should be saved in cosmos as:

{
  "id": "xxx",
  "content": "long content here",
  "tags": ["tag1", "tag2"],
  ...
}

I know I have to configure something in the OnModelCreating(ModelBuilder modelBuilder) of the Context. But I cannot get it setup correctly.

I've tried to following options (and several other ToJsonProperty methods):

  • modelBuilder.Entity<Post>().OwnsMany(p => p.Tags);
  • modelBuilder.Entity<Post>().OwnsMany<string>(p => p.Tags);

Eventually I want to be able to query based on those tags, any help or pointers in the right direction are greatly appreciated!

I also found this answer but converting the array to a comma separated string would defeat the purpose (since that doesn't allow us to query those posts).

Someone else asked roughly the same question on the Microsoft forums, where a Microsoft employee states that in pure CosmosDB it is possible to embed a string array in cosmos.

Kyliekylila answered 13/11, 2020 at 10:39 Comment(0)
K
3

I found a github issue tracking this issue at the EF core provider for Cosmos.

So the Official answer is, this is currently unsupported. At least until the mentioned issue is closed.

Update, July 2021, it seems that this is now supported in EfCore 6.0.0

Kyliekylila answered 13/11, 2020 at 12:29 Comment(0)
P
3

I recently tried to find an answer to the same question. Did not find a good solution, so I created a new model

public class Tag
{
   public string Name { get; set; }
}

public class Post
{
  ...
  public IEnumerable<Tag> Tags { get; set; }
}

...
modelBuilder.Entity<Post>().OwnsMany(p => p.Tags);
Procopius answered 13/11, 2020 at 22:17 Comment(1)
While this is a great way around the issue, the main issue is still that a simple string collection isn’t supported. Not sure what the best answer is.Kyliekylila
V
0

Use a value converter like this:


public class TagsValueConverter : ValueConverter<IReadOnlyCollection<string>, string[]>
{
    public TagsValueConverter() : base(
        value => value.ToArray(),
        dbValue => dbValue.ToList())
    {
    }
}
Vinous answered 14/3, 2022 at 7:16 Comment(2)
And how do we use this ValueConverter? I think this wasn't available before and starting 6.0 your dont need custom things anymore since it's now build in? github.com/dotnet/efcore/issues/14762Kyliekylila
The value converter I think was there even for ef core 3.1, but the issue is still there, I will open a new issue on the github since yesterday I had the issue with ef core 6.0.3 github.com/dotnet/efcore/pull/25344#issuecomment-1066466185Vinous
C
-1

In your model builder use a value converter like this:

modelBuilder.Entity<Post>().Property(p => p.Tags).HasConversion(v => JsonConvert.SerializeObject(v), v => JsonConvert.DeserializeObject<List<string>>(v));
Confine answered 20/7, 2021 at 4:8 Comment(2)
This work-around is already mentioned in the answer that I'm referring to https://mcmap.net/q/1624705/-how-do-i-store-a-property-of-ienumerable-lt-string-gt-in-a-cosmos-table-with-ef-core-3-1 and the problem is that you can then save the data, but you have no way to query based on that data, which is part of the question. example: Get posts that have tag x?Kyliekylila
also you could use a converter from IReadOnlyCollection<string> to string[], and it will work with CosmosDB provider, medium.com/@shahabganji/…Vinous

© 2022 - 2024 — McMap. All rights reserved.