Is there a way to fill a dictionary property with Entity Framework Core?
For performance reasons, we like to search in the application instead of the database. As a list won’t scale well, we like to use a dictionary.
For example (simplified example)
class Course
{
public Dictionary<string, Person> Persons { get; set; }
public int Id { get; set; }
}
class Person
{
public string Firstname { get; set; }
public string Lastname { get; set; }
}
Things I tried
System.InvalidOperationException: The property 'Persons' could not be mapped, because it is of type 'Dictionary' which is not a supported primitive type or a valid entity type. Either explicitly map this property, or ignore it using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
Try adding a value conversion (with HasConversion
), but conversion one only works on a single item and not on collections. The HasMany
already gives a compile error:
builder
.HasMany<Person>(c => c.Persons) //won't compile, Persons isn't a IEnumerable<Person>
.WithOne().HasForeignKey("PersonId");
Creating a custom collection class (inherited from Collection<T>
and implement InsertItem
, SetItem
etc.) – unfortunately this also won’t work because EF Core will add the item to the collection and first after that will fill the properties (at least with our OwnsOne properties, that is not in the demo case) - SetItem
won't be called afterwards.
Adding a "computed" property that will build the dictionary, the setter won't be called (the list is updated every time with partly values, a bit the same as above). See try:
class Course
{
private Dictionary<string, Person> _personsDict;
public List<Person> Persons
{
get => _personsDict.Values.ToList();
set => _personsDict = value.ToDictionary(p => p.Firstname, p => p); //never called
}
public int Id { get; set; }
}
Of course I could build a dictionary in the Repository (using the Repository pattern), but that’s tricky as I could forget some parts – and I really prefer compile time errors over run-time errors and declarative style over imperative style code.
Update, to be clear
List
instead of Dictionary, the mapping works Item[] Property. This property is used to get or set the value associated with the specified key in the Dictionary. Here, key is the Key of the value to get or set.
Entity Framework does not presently support mapping a Dictionary natively.
Keep using EF6 if the data access code is stable and not likely to evolve or need new features. Port to EF Core if the data access code is evolving or if the app needs new features only available in EF Core. Porting to EF Core is also often done for performance.
Shadow properties are properties that aren't defined in your . NET entity class but are defined for that entity type in the EF Core model. The value and state of these properties are maintained purely in the Change Tracker.
Seems someone has been struggling with that and found solution. See: Store a Dictionary as a JSON string using EF Core 2.1
The definition of the entity is as follows:
public class PublishSource
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public Dictionary<string, string> Properties { get; set; } = new Dictionary<string, string>();
}
In the OnModelCreating method of the database context I just call HasConversion, which does the serialization and deserialization of the dictionary:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<PublishSource>()
.Property(b => b.Properties)
.HasConversion(
v => JsonConvert.SerializeObject(v),
v => JsonConvert.DeserializeObject<Dictionary<string, string>>(v));
}
One important thing I have noticed, however, is that when updating the entity and changing items in the dictionary, the EF change tracking does not pick up on the fact that the dictionary was updated, so you will need to explicitly call the Update method on the DbSet<> to set the entity to modified in the change tracker.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With