EF CORE 2.1 HasConversion для всех свойств типа datetime

Ранее я использовал DateTimeKindEntityMaterializerSource (Git), чтобы преобразовать все даты и время в UTC при чтении сущностей, потому что по умолчанию был неопределенным.

В EF core 2.1 DateTimeKindEntityMaterializerSource больше не работает, но мы действительно можем это сделать.

         builder
        .Entity<ESDataQuotation>()
        .Property(e => e.CreatedDate)
        .HasConversion(v => v, v => DateTime.SpecifyKind(v, DateTimeKind.Utc));

Однако у меня есть много свойств DateTime, и я хотел бы, чтобы был способ сделать преобразование для всех свойств типа DateTime.


person Pilouk    schedule 06.06.2018    source источник


Ответы (3)


Выдержка из раздела документации EF Core 2.1 преобразования значений:

В настоящее время нет способа указать в одном месте, что каждое свойство данного типа должно использовать один и тот же преобразователь значений. Эта функция будет рассмотрена для будущего выпуска.

До тех пор вы можете использовать типичный цикл в конце переопределения OnModelCreating, где обнаруживаются все типы сущностей и свойства:

var dateTimeConverter = new ValueConverter<DateTime, DateTime>(
    v => v, v => DateTime.SpecifyKind(v, DateTimeKind.Utc));

foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
    foreach (var property in entityType.GetProperties())
    {
        if (property.ClrType == typeof(DateTime) || property.ClrType == typeof(DateTime?))
            property.SetValueConverter(dateTimeConverter);
    }
}
person Ivan Stoev    schedule 06.06.2018
comment
Это выдаст предупреждение, если вы установили warningConfigurationBuilder.Throw(RelationalEventId.QueryClientEvaluationWarning) в DbContext optionsBuilder: параметр или литерал SQL был сгенерирован для типа «DateTime» с использованием ValueConverter «ValueConverter‹DateTime, DateTime›». Проверьте сгенерированный SQL на правильность и вместо этого рассмотрите возможность оценки целевого выражения в памяти. Но не беспокойтесь, сгенерированный SQL-запрос верен, поэтому вам следует игнорировать это предупреждение. - person Dominik; 26.04.2019

Просто подумал, что могу бросить свои пять копеек

Здесь открыта проблема: https://github.com/aspnet/EntityFrameworkCore/issues/10784

Решение Ивана будет работать для простых типов, таких как DateTime и т. д., но произойдет сбой при использовании пользовательских типов при вызове entityType.GetProperties(), это лучше описано в проблеме по ссылке выше. Чтобы заставить его работать с пользовательскими типами, вам придется использовать entityType.ClrType.GetProperties().

Для универсального обходного пути вы можете использовать этот метод расширения:

public static class ModelBuilderExtensions
{
    public static ModelBuilder UseValueConverterForType<T>(this ModelBuilder modelBuilder, ValueConverter converter)
    {
        return modelBuilder.UseValueConverterForType(typeof(T), converter);
    }

    public static ModelBuilder UseValueConverterForType(this ModelBuilder modelBuilder, Type type, ValueConverter converter)
    {
        foreach (var entityType in modelBuilder.Model.GetEntityTypes())
        {
            var properties = entityType.ClrType.GetProperties().Where(p => p.PropertyType == type);
            foreach (var property in properties)
            {
                modelBuilder.Entity(entityType.Name).Property(property.Name)
                    .HasConversion(converter);
            }
        }

        return modelBuilder;
    }
}
person Konrad    schedule 05.09.2018
comment
Это единственное, что сработало при попытке сделать это со списками и словарями. - person agritton; 02.06.2019
comment
@agritton, можете ли вы поделиться здесь своим решением для обработки списков и словарей? - person mreyeros; 26.09.2019
comment
@mreyeros уверен, что это код. Я использую его для преобразования своих списков и словарей в столбцы json. На самом деле это не вписывается в комментарий, поэтому я просто добавлю ответ. - person agritton; 27.09.2019
comment
Я думаю, что это правильный ответ. Спасибо, @Конрад! - person Eugen Kotov; 02.09.2020

Это не поместилось бы в разделе комментариев, поэтому я добавил ответ. Вот код, который я использую для преобразования списков и словарей.

foreach (var entity in builder.Model.GetEntityTypes())
{
    foreach (var property in entity.ClrType.GetProperties())
    {
        if (property.PropertyType == typeof(List<string>))
        {
            builder.Entity(entity.Name).Property(property.Name).HasConversion(new ValueConverter<List<string>, string>(v => v.ToJson(), v => v.FromJson<List<string>>())).HasColumnType("json");
        }
        else if (property.PropertyType == typeof(Dictionary<string, string>))
        {
            builder.Entity(entity.Name).Property(property.Name).HasConversion(new ValueConverter<Dictionary<string, string>, string>(v => v.ToJson(), v => v.FromJson<Dictionary<string, string>>())).HasColumnType("json");
        }
        else if (property.PropertyType == typeof(List<List<string>>))
        {
            builder.Entity(entity.Name).Property(property.Name).HasConversion(new ValueConverter<List<List<string>>, string>(v => v.ToJson(), v => v.FromJson<List<List<string>>>())).HasColumnType("json");
        }
        else if (property.PropertyType == typeof(bool))
        {
            builder.Entity(entity.Name).Property(property.Name).HasConversion(new BoolToZeroOneConverter<short>());
        }
    }
}
person agritton    schedule 27.09.2019