Entity Framework Concurrency Token DateTime类型的问题

时间:2012-02-21 17:45:00

标签: entity-framework ef-code-first code-first

我遇到了DateTime的并发令牌问题。这是重现问题的简单方法。有一个实体:

public class Employee
{
    public int EmployeeID { get; set; }
    public string Name { get; set; }
    [ConcurrencyCheck]
    public DateTime LastModified { get; set; }
}

一个简单的DbContext:

public class MyContext : DbContext
{
    public DbSet<Employee> Employees { get; set; }
}

以下代码:

Employee orig;

//  Create a row (insert)
using (var context = new MyContext())
{
    orig = new Employee
    {
        Name = "Mike",
        LastModified = DateTime.Now
    };
    context.Employees.Add(orig);

    context.SaveChanges();
}
//  Update the row, passing the right concurrency token
using (var context = new MyContext())
{
    var clone = new Employee
    {
        EmployeeID = orig.EmployeeID,
        Name = "Suzanne",
        //  Pass the concurrency token here
        LastModified = orig.LastModified
    };
    context.Employees.Attach(clone);
    //  Mark the entity as modified to force an update
    context.Entry(clone).State = EntityState.Modified;

    //  Boom!  Currency exception!
    context.SaveChanges();
}

基本上,我创建一个员工,然后更新它。砰!我查看在SQL(Profiling)上生成的更新语句:

exec sp_executesql N'update [dbo].[Employees]
set [Name] = @0, [LastModified] = @1
where (([EmployeeID] = @2) and ([LastModified] = @3))
',N'@0 nvarchar(max) ,@1 datetime2(7),@2 int,@3 datetime2(7)',@0=N'Suzanne',@1='2012-02-21 
12:06:30.0141536',@2=0,@3='2012-02-21 12:06:30.0141536'

声明对我来说听起来很合理,但它失败了,即它修改了零行,好像([LastModified] = @ 3)失败了。

我怀疑是'精确问题',即与存储的数字不匹配的位数。它可能是.NET和SQL中的DateTime表示不匹配吗?

我尝试在我的Poco类中使用System.Data.SqlTypes.SqlDateTime而不是DateTime,希望这会带来正确的精度,但我无法映射它,EF总是将属性取消映射。

解决方案?

1 个答案:

答案 0 :(得分:4)

我发现了问题!实际上,这里有两个问题:技术问题和语义问题。

技术问题是,无论出于何种原因,EF都会将System.DateTime作为datetime(2)SQL类型发送给SQL。默认情况下,它会将System.DateTime映射为datetime。尽管强制SQL类型为datetime(2),但实际上我没有成功让EF使用datetime(2)创建数据库。但如果你事后改变它,它就解决了这个问题。所以这个问题确实是一个精确的问题。

语义问题是,如果你考虑它,整个没有意义。您需要将并发令牌传递给SQL,以证明您是最后一个读取该表的人。但是,每次更新行时都需要更新并发令牌。一个排除了另一个:如果你尝试将LastModified更新为DateTime.Now,你会有一个并发异常,因为并发令牌不是存储在行中的那个!

因此,尽管找到了技术问题的解决方案,但整个方案没有意义。

......除非!您可以找到一种在不使用EF的情况下更新LastModified列的方法。例如,你可以有一个触发器。通常你不想去那里。