Use correct value comparers for collection properties with value conversion
See original GitHub issueMy entity has a collection of SmartEnums (simplified in the code example below) which is persisted in the database using a ValueConverter in order to keep the domain model clean (I wanted to avoid introducing a wrapper entity with an extra ID property). It works correctly for retrieving data but unfortunately doesn’t seem to pick up changes to the collection automatically. When calling SaveChanges, the changes are not persisted unless the entity state is manually set to EntityState.Modified beforehand.
Steps to reproduce
MySmartEnum
public class MySmartEnum
{
public string Value { get; set; }
public static MySmartEnum FromValue(string value)
{
return new MySmartEnum { Value = value };
}
}
Entity
public class Entity
{
public Entity()
{
SmartEnumCollection = new HashSet<MySmartEnum>();
}
public int Id { get; set; }
public virtual ICollection<MySmartEnum> SmartEnumCollection { get; set; }
}
Context
public class TestContext : DbContext
{
public TestContext()
{ }
public TestContext(DbContextOptions<TestContext> options)
: base(options)
{ }
public virtual DbSet<Entity> Entities { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var valueConverter = new ValueConverter<ICollection<MySmartEnum>, string>
(
e => string.Join(',', e.Select(c => c.Value)),
str => new HashSet<MySmartEnum>(
str.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(x => MySmartEnum.FromValue(x)))
);
modelBuilder.Entity<Entity>()
.Property(e => e.SmartEnumCollection)
.HasConversion(valueConverter);
}
}
Update method
using (var context = new TestContext(options))
{
var entity = context.Entities
.First();
entity.SmartEnumCollection.Add(MySmartEnum.FromValue("Test"));
// Changes are persisted only if the following line is uncommented:
//context.Entry(entity).State = EntityState.Modified;
context.SaveChanges();
}
Further technical details
EF Core version: 3.0.0-rc1.19427.8 Database Provider: Microsoft.EntityFrameworkCore.SqlServer (Version 3.0.0-rc1.19427.8) Operating system: Windows 10 (Version 10.0.18362.295) IDE: Visual Studio 2019 16.2.3
Issue Analytics
- State:
- Created 4 years ago
- Reactions:5
- Comments:9 (4 by maintainers)
Top Related StackOverflow Question
@BalintBanyasz The issue here is that EF Core needs to be able to create a snapshot of the current value and then compare that snapshot with the new value to see if it has changed. This requires special code when using value conversion to map a type with significant structure–in this case the
ICollection<>.The fix for this is to tell EF how to snapshot and compare these collections. For example:
Notes for triage: we should consider:
IEnumerable), then we could automatically use a value comparer like shown above.Note from triage:
IEquatableorIComparableand use it, only generating the snapshotting?