Home > Software engineering >  How to implement EF Core ValueComparer for List<TModel> JSON converter?
How to implement EF Core ValueComparer for List<TModel> JSON converter?

Time:09-27

I stumbled upon the compiled models paragraph of the MSDN article about Advanced Performance Topics regarding Entity Framework Core and wanted to try it out. When running the dotnet ef dbcontext optimize command in the command line, I get the following error:

The property 'FooModel.BarProperty' has a value comparer configured using a ValueComparer instance. Instead, create types that inherit from ValueConverter and ValueComparer and use 'HasConversion=<ConverterType, ComparerType=>()' or 'HasConversion(Type converterType, Type comparerType)' to configure the value converter and comparer.

The converter for BarProperty is currently configured in the overwritten OnModelCreating method of my DbContext class:

modelBuilder.Entity<FooModel>()
            .Property(x => x.BarProperty)
            .HasConversion(
                v => JsonSerializer.Serialize(v, (JsonSerializerOptions)null),
                v => JsonSerializer.Deserialize<List<BarModel>>(v, (JsonSerializerOptions)null),
                    new ValueComparer<List<BarModel>>(
                        (c1, c2) => c1.SequenceEqual(c2),
                        c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())),
                        c => c.ToList()));

This code is responsible for saving (serializing / deserializing) some related objects as JSON in a column and works like a charm. But I am struggeling to move / translate this inline-code to custom ValueConverter<TModel,TProvider> and ValueComparer<T> implementations. Another reason why I want to prevent the inline-code is that it is very repetetive and used multiple times in the exact way for different properties.

I don't even know if I need to implement the generic ValueConverter<List<BarModel>, string> or just ValueConverter. Neither do I know which generic types I have to define (especially TProvider is confusing me).

Thanks in advance for your precious help!

CodePudding user response:

Thats how I solved it.

In my DbContext:

protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
    base.ConfigureConventions(configurationBuilder);
    configurationBuilder.Properties<List<FooModel>>().HaveConversion<FooModelCoverter, FooModelComparer>();
}

Comparer:

internal class FooModelComparer : ValueComparer<List<FooModel>>
{
    public FooModelComparer()
        : base((c1, c2) => c1.SequenceEqual(c2),
                c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())),
                c => c.ToList())
    { }
}

internal class FooModelCoverter : ValueConverter<List<FooModel>, string>
{
    public FooModelCoverter() : 
        base(v => JsonSerializer.Serialize(v, (JsonSerializerOptions)null),
             v => JsonSerializer.Deserialize<List<FooModel>>(v, (JsonSerializerOptions)null), 
             null)
    { }
}
  • Related