-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use correct value comparers for collection properties with value conversion #17471
Comments
@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 The fix for this is to tell EF how to snapshot and compare these collections. For example: var valueComparer = new ValueComparer<ICollection<MySmartEnum>>(
(c1, c2) => c1.SequenceEqual(c2),
c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())),
c => (ICollection<MySmartEnum>)c.ToHashSet());
modelBuilder.Entity<Entity>()
.Property(e => e.SmartEnumCollection)
.HasConversion(valueConverter)
.Metadata.SetValueComparer(valueComparer); Notes for triage: we should consider:
|
Note from triage:
|
Thank you @ajcvickers. It works perfectly with the |
3.1 part of this (warning) split out into #18600 |
Fox me the ValueComparer was not working, I had to remove |
+1 for better documentation and +1 for an easier solution. I have not tested this but I think a string backing field could be used in junction with a getter/setter that will serialize/deserialize to and from the backing field. Either way both are less than ideal imho. |
I tried to follow @ajcvickers's advice. But my code doesn't work properly. public class Person
{
public string Id { get; set; }
public virtual List<int> Numbers { get; set; }
public Person()
{
Id = Guid.NewGuid().ToString();
Numbers = new List<int>();
}
}
public class PersonEntityTypeConfiguration : IEntityTypeConfiguration<Person>
{
internal static readonly ValueConverter<List<int>, string> Converter
= new ValueConverter<List<int>, string>(
v => JsonSerializer.Serialize(v, null),
v => JsonSerializer.Deserialize<List<int>>(v, null));
public void Configure(EntityTypeBuilder<Person> builder)
{
builder.HasKey(x => x.Id);
builder
.Property(x => x.Numbers)
.HasDefaultValue(new List<int>())
.IsRequired()
.HasConversion(Converter)
.Metadata.SetValueComparer(new ValueComparer<List<int>>(
(c1, c2) => c1.SequenceEqual(c2),
c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())),
c => c.ToList()));
}
}
var person = dbContext.Persons.FirstOrDefault(x => x.Id == "6c7a5476-0266-459e-8229-544483379108");
person.Numbers.Add(98);
await dbContext.SaveChangesAsync(); I get "person" entity from database then I add new item to "Numbers" list. But SaveChanges doesn't save new values in the collection to database. I have test project with test database https://github.com/samburovkv/EfSQLiteTests I'd like to know how this feature works. var person = dbContext.Persons.FirstOrDefault(x => x.Id == "6c7a5476-0266-459e-8229-544483379108");
person.Numbers = new List<int>(person.Numbers);
person.Numbers.Add(98);
await dbContext.SaveChangesAsync(); |
@samburovkv I moved this to a new issue: #21986 |
equalsExpression paramters are defined as nullable and this code |
Yes! For example, for
Yes my analysers also caught that bit. And one cannot use null propagation in the expression tree, so I just used null-forgiving ( |
My 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 callingSaveChanges
, the changes are not persisted unless the entity state is manually set toEntityState.Modified
beforehand.Steps to reproduce
MySmartEnum
Entity
Context
Update method
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
The text was updated successfully, but these errors were encountered: