实体框架映射DateTimeOffset到SQL Server DateTime

12

有没有一种方法可以将DateTimeOffset属性映射到SQL Server的datetime列,假设你不能更改任何一方,也就是说,属性和列必须保持这些日期类型?

我知道最简单的方法是使它们匹配,但想知道是否有一种方法可以解决这个问题。我研究了自定义映射,但似乎我必须自己映射所有的列,而不仅仅是DateTimeOffset属性。

我尝试了:

modelBuilder.Entity<Customer>().Property(c => c.LastModifiedOn).HasColumnType("datetime");

但是那样会抛出“指定的成员映射无效”错误。

我希望能够将UtcDateTime DateTimeOffset属性值存储在数据库中,读取时将DateTimeOffset转换为UTC时间(即偏移量为零)。

1个回答

19

.NET中的DateTimeOffset类将映射到DateTimeOffset SQL类型。你无法直接更改此行为,因为EF不提供简单的类型转换/映射。如果您想将其存储为DateTime,您必须使用hack的方法。

首先,使用@cincura.net在这篇文章中引用的技巧定义Customer类以公开私有属性以供映射。

public class Customer
{
    public static class CustomerExpressions
    {
        public static readonly Expression<Func<Customer, DateTime>> LastModifiedOn = c => c.LastModifiedOnInternal;
    }

    // Other properties

    public DateTimeOffset LastModifiedOn
    {
        get { return new DateTimeOffset(LastModifiedOnInternal); }
        set { LastModifiedOnInternal = value.DateTime; }
    }

    private DateTime LastModifiedOnInternal { get; set; }
}

现在你有两个属性 - 一个是私有的,保存着你想要持久化到数据库中的DataTime,另一个是公开的,暴露出DateTimeOffset给你的应用程序。在上下文中定义它:

public class Context : DbContext
{
    public DbSet<Customer> Customers { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Customer>().Ignore(c => c.LastModifiedOn);
        modelBuilder.Entity<Customer>().Property(Customer.CustomerExpressions.LastModifiedOn).HasColumnName("LastModifiedOn");
    }
}

为什么你不直接使用DateTime并将其存储为UTC时间?


感谢您的回复。我们需要处理多个时区,使用DateTimeOffset进行算术运算比在进行任何时间计算之前将所有内容转换为UTC更容易。 - Carlos
10
避免在EF中使用DateTime的一个原因是,其值无法回传:所有值都以DateTimeKind.Unspecified加载。没有办法告诉EF它应该默认为UTC时间。虽然有一些hack可以修复这个问题,但它们很丑陋,并且可能会影响性能(例如使用ObjectContext事件)。 - Søren Boisen
@SørenBoisen - 在我的迁移期间创建表时,我只需添加 defaultValueSql: "GETUTCDATE()" - Suamere
CreatedDate = c.DateTime(nullable: false, precision: 7, storeType: "datetime2", defaultValueSql: "GETUTCDATE()"), - Suamere

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