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

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

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

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

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

Ответы

Ответ 1

Выдержка из раздела документации по конверсии значений 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);
    }
}

Ответ 2

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

Здесь открыта проблема для этого: 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;
    }
}

Ответ 3

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

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>());
                }
            }
        }