我能把一个 SQL 数据类型的 DateTime 转换成 EF 中的 DateTimeOffSet 吗?

3
我有一个庞大的数据库,我们需要进行大量的时区转换。我正在逐步将数据库转换为使用DateTimeOffset数据类型,但这是我无法全面实现的。改变太多了。
但是,我可以改变代码,并且我知道我的所有日期都存储在数据库中的UTC时间,因此我想在.NET中使用DateTimeOffset对象。
如何让EF在运行时帮助我完成转换?
我尝试过这个:
modelBuilder.Properties<DateTimeOffset>()
                .Configure( c => c.HasColumnType( "datetime" ) );

但我遇到了错误

(37,12):错误2019:指定的成员映射无效。类型“Edm.DateTimeOffset [Nullable = True,DefaultValue =,Precision =]”的成员“ModifyDate”在类型“{ObjectType}”中与类型“SqlServer.datetime [Nullable = True,DefaultValue =,Precision = 3]”的成员“ModifyDate”不兼容。在类型“CodeFirstDatabaseSchema。{ObjectType}”中。


类似问题 https://dev59.com/wWox5IYBdhLWcg3wKBN9 - Arsen Mkrtchyan
@ArsenMrkt - 如果我按照那个帖子中的被接受的答案去做,我会得到一个新的错误:"属性 'CreateDate' 在类型 '{ObjectType}' 上未声明。请验证该属性是否已通过使用 Ignore 方法或 NotMappedAttribute 数据注释明确地从模型中排除。确保它是有效的基元属性。" - Russ
1个回答

6

以下是一个值得考虑的方法:

首先,定义以下属性:

[AttributeUsage(AttributeTargets.Property)]
public class DateTimeKindAttribute : Attribute
{
    private readonly DateTimeKind _kind;

    public DateTimeKindAttribute(DateTimeKind kind)
    {
        _kind = kind;
    }

    public DateTimeKind Kind
    {
        get { return _kind; }
    }

    public static void Apply(object entity)
    {
        if (entity == null)
            return;

        var properties = entity.GetType().GetProperties()
            .Where(x => x.PropertyType == typeof(DateTime) || x.PropertyType == typeof(DateTime?));

        foreach (var property in properties)
        {
            var attr = property.GetCustomAttribute<DateTimeKindAttribute>();
            if (attr == null)
                continue;

            var dt = property.PropertyType == typeof(DateTime?)
                ? (DateTime?) property.GetValue(entity)
                : (DateTime) property.GetValue(entity);

            if (dt == null)
                continue;

            property.SetValue(entity, DateTime.SpecifyKind(dt.Value, attr.Kind));
        }
    }
}

现在将这个属性与你的EF上下文连接起来:
public class MyContext : DbContext
{
    public DbSet<Foo> Foos { get; set; }

    public MyContext()
    {
        ((IObjectContextAdapter)this).ObjectContext.ObjectMaterialized +=
            (sender, e) => DateTimeKindAttribute.Apply(e.Entity);
    }
}

现在,对于任何 DateTimeDateTime? 属性,您都可以应用此属性:

public class Foo
{
    public int Id { get; set; }

    [DateTimeKind(DateTimeKind.Utc)]
    public DateTime Bar { get; set; }
}

在这种情况下,每当实体框架从数据库加载实体时,它都会设置您指定的DateTimeKind,例如UTC。
现在,你说想开始转换为DateTimeOffset类型。你可以利用DateTime具有向DateTimeOffset的单向隐式转换,并且会考虑.Kind属性这一事实。换句话说,您可以这样做:
DateTimeOffset BarDTO = foo.Bar;

即使foo.Bar是一个DateTime,它也可以正常工作。因为种类被设置为UTC,在DateTimeOffset中偏移量将被设置为零。

当然,您还可以在您的模型中使用以下代码来实现这个功能:

[NotMapped]
public DateTimeOffset BarDTO
{
    get { return Bar; }
    set { Bar = value.UtcDateTime; }
}

我相信你可以根据需要进行变化。重要的是,无论将哪个属性映射到该字段,类型都必须匹配。

不完全是我期望的,但是在正确方向上迈出了相当大的一步。 - Russ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接