EF6与SQL Server 2016 DateTime字段并发检查问题求助
我之前也踩过这个坑!SQL Server 2016在datetime类型的精度处理上和2012有细微差异,这几乎肯定是导致你并发检查失败的根源。下面是我整理的几个排查和解决步骤:
1. 先确认EF生成的参数类型是否匹配
EF有时候会“自作主张”把.NET的DateTime参数映射为datetime2类型,哪怕你已经用Fluent API指定了列类型是datetime。这种情况下,数据库里存储的是经过舍入的datetime值,但EF用高精度的datetime2参数去对比,自然会不匹配导致并发冲突。
验证方法:打开EF的日志功能,查看更新操作生成的SQL语句:
// 在你的DbContext构造函数里添加日志输出 public YourDbContext() { this.Database.Log = s => System.Diagnostics.Debug.WriteLine(s); }
运行更新操作后,看日志里的WHERE子句参数,如果是@p0 datetime2而不是@p0 datetime,那就是这个问题。
解决办法:在Fluent API里显式指定参数的DbType,或者全局配置所有DateTime属性的列类型和参数类型:
// 全局配置(针对EF6) protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); // 强制所有DateTime属性使用datetime类型 modelBuilder.Properties<DateTime>() .Configure(c => c.HasColumnType("datetime") .HasDbType(System.Data.DbType.DateTime)); // 单独配置你的LastModificationTime(如果全局配置不够的话) modelBuilder.Entity<YourEntity>() .Property(e => e.LastModificationTime) .HasColumnName("MODIFQUAND") .HasColumnType("datetime") .HasDbType(System.Data.DbType.DateTime) .IsConcurrencyToken(); }
2. 手动截断DateTime到SQL datetime的精度
SQL Server的datetime类型精度只有3.33毫秒(每个刻度是3333333 ticks),而.NET的DateTime精度是100纳秒。直接赋值DateTime.Now的话,存储到数据库时会被舍入,但EF在并发检查时会用原始的高精度值去对比,导致不匹配。
解决办法:写一个扩展方法,把DateTime截断到符合SQL datetime的精度:
public static class DateTimeExtensions { public static DateTime ToSqlDateTimePrecision(this DateTime dateTime) { // SQL datetime的每个刻度是3333333 ticks var roundedTicks = (long)Math.Round(dateTime.Ticks / 3333333.0) * 3333333; return new DateTime(roundedTicks, dateTime.Kind); } }
然后在更新实体的时候,用这个方法赋值:
entity.LastModificationTime = DateTime.Now.ToSqlDateTimePrecision();
3. 检查数据库兼容级别(临时排查用)
SQL Server 2016默认兼容级别是130,而2012是110。虽然不推荐长期使用,但可以临时把兼容级别降到110,看看是否解决问题,以此确认是不是兼容级别带来的差异:
ALTER DATABASE YourDatabase SET COMPATIBILITY_LEVEL = 110;
如果这样能解决,说明你需要针对130级别调整代码(还是建议用前面的方法,而不是一直降低兼容级别)。
4. 升级EF版本
如果你用的是比较老的EF版本(比如EF6.0以前),可能存在对SQL Server 2016的兼容性bug。尝试升级到EF6的最新稳定版本,很多这类问题都已经被修复了。
最后验证
修改后,再通过EF日志查看生成的SQL,确保WHERE子句里的LastModificationTime参数是datetime类型,并且赋值的时间已经被截断到正确精度,这样并发检查就能正常工作了。
内容的提问来源于stack exchange,提问作者Adrien

