'Configure EF Core with Cosmos DB to store ICollection of string

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.



Solution 1:[1]

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

Solution 2:[2]

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);

Solution 3:[3]

Use a value converter like this:


public class TagsValueConverter : ValueConverter<IReadOnlyCollection<string>, string[]>
{
    public TagsValueConverter() : base(
        value => value.ToArray(),
        dbValue => dbValue.ToList())
    {
    }
}

Solution 4:[4]

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));

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Roman Marusyk
Solution 3 Saeed Ganji
Solution 4 T.J. McNaboe