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.

Stephan
  • 2,356
  • 16
  • 38

4 Answers4

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

Stephan
  • 2,356
  • 16
  • 38
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);
Roman Marusyk
  • 23,328
  • 24
  • 73
  • 116
  • 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. – Stephan Nov 14 '20 at 10:18
0

Use a value converter like this:


public class TagsValueConverter : ValueConverter<IReadOnlyCollection<string>, string[]>
{
    public TagsValueConverter() : base(
        value => value.ToArray(),
        dbValue => dbValue.ToList())
    {
    }
}
Saeed Ganji
  • 197
  • 17
  • 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? https://github.com/dotnet/efcore/issues/14762 – Stephan Mar 14 '22 at 09:38
  • 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 https://github.com/dotnet/efcore/pull/25344#issuecomment-1066466185 – Saeed Ganji Mar 14 '22 at 13:27
-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));
  • This work-around is already mentioned in the answer that I'm referring to https://stackoverflow.com/a/62557600/639153 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? – Stephan Jul 20 '21 at 08:42
  • also you could use a converter from IReadOnlyCollection to string[], and it will work with CosmosDB provider, https://medium.com/@shahabganji/store-array-of-strings-with-ef-core-cosmosdb-8c69321b3983 – Saeed Ganji Mar 14 '22 at 07:14