EF CORE 2.1 HasConversion对datetime类型的所有属性

Pil*_*ouk 14 c# entity-framework-core asp.net-core

我以前使用DateTimeKindEntityMaterializerSource(Git)在读取实体时将所有DateTime转换为UTC,因为默认是未指定的.

使用EF核心2.1,DateTimeKindEntityMaterializerSource不再有效,但我们实际上可以这样做

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

但是,我有许多DateTime的属性,我想如果有一种方法可以为DateTime类型的所有属性进行转换.

Iva*_*oev 26

摘自EF Core 2.1 Value Conversions文档主题:

目前无法在一个地方指定给定类型的每个属性必须使用相同的值转换器.此功能将在未来版本中考虑使用.

在此之前,您可以使用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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 如果您在DbContext optionsBuilder上设置了warningConfigurationBuilder.Throw(RelationalEventId.QueryClientEvaluationWarning),则会发出警告:“使用ValueConverter'ValueConverter &lt;DateTime,DateTime&gt;'为'DateTime'类型生成了SQL参数或文字。请查看生成的SQL的正确性,请考虑在内存中评估目标表达式。” 但是不用担心生成的SQL查询是正确的,因此您应该忽略此警告。 (3认同)

Dyn*_*lon 26

从 EF core 开始v6.0.0-preview6,有一个更优雅的解决方案来全局注册 ValueConverter。在此示例中,我使用自定义 ISO8601 转换器,该转换器可在 UTC 格式的 ISO8601 之间进行转换,并始终Z在末尾附加:

public class DateTimeToIso8601StringConverter : ValueConverter<DateTime, string>
{
    public DateTimeToIso8601StringConverter() : base(Serialize, Deserialize, null)
    {
    }
            
    static Expression<Func<string, DateTime>> Deserialize = x => DateTime.Parse(x).ToUniversalTime();
    static Expression<Func<DateTime, string>> Serialize = x => x.ToString("o", System.Globalization.CultureInfo.InvariantCulture);
}
Run Code Online (Sandbox Code Playgroud)

在你的DbContext班级中:

protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
    configurationBuilder.Properties<DateTime>().HaveConversion<DateTimeToIso8601StringConverter>();
}
Run Code Online (Sandbox Code Playgroud)

这避免了必须为每个模型属性单独指定转换器。


Kon*_*rad 5

只是以为我可以投入两分钱

在这里打开了一个问题:https : //github.com/aspnet/EntityFrameworkCore/issues/10784

Ivan的解决方案适用于诸如DateTimeetc之类的简单类型,但是在调用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;
    }
}
Run Code Online (Sandbox Code Playgroud)