为什么AsNoTracking影响DateTime精度?

时间:2016-12-22 14:46:44

标签: sql-server entity-framework-6

给出以下代码:

var dbRecords = _context.Alerts.AsNoTracking()
                        .Where(a => a.OrganizationId == _authorization.OrganizationId)
                        .ToList();
var dbRecords2 = _context.Alerts
                         .Where(a => a.OrganizationId == _authorization.OrganizationId)
                         .ToList();

foreach (var untrackedRecord in dbRecords) {
    var trackedRecord = dbRecords2.First(a => a.Id == untrackedRecord.Id);

    Assert.AreEqual(untrackedRecord.TimeStamp.Ticks, trackedRecord.TimeStamp.Ticks);
}

TimeStamp数据存储在SQL Server 2012中定义为datetime2(0)的列中。

Assert失败,调试器证明两个Ticks值总是不同。

Expected: 636179928520000000 But was:  636179928523681935

未跟踪的值将始终四舍五入到最接近的秒(根据SQL存储的内容,这是预期的)。创建记录时,我保存的值来自DateTime.Now。

测试更多,对于我正在测试的每个对象,这似乎都不正确(不一致的刻度),仅适用于我最近插入的记录。查看代码并给出列定义的方式,对我来说这并不重要。

现在,为了让我的测试通过,我只是将DateTime值比较到第二个,这就是所需要的。但是,我只是想了解为什么会发生这种情况:为什么我不能可靠地比较两个DateTime值,具体取决于是否跟踪实体?

1 个答案:

答案 0 :(得分:1)

我想出来了,所以回答我自己的问题;我发现我离开了这里的关键信息。我提到这个问题出现在测试中。我没有提到的是我们在单个事务中插入记录然后测试所有记录,并在单个DbContext中。

因为我对所有工作都使用相同的DbContext,所以缓存了为测试而插入的Alert对象。当我使用AsNoTracking查询对象时,DbContext必须刷新对象,然后再将它们发回给我(因为它们的当前状态没有被跟踪,因此EF不知道),显然没有更新缓存中的内容(因为我们告诉EF我们不想跟踪对象。)

在没有AsNoTracking的情况下查询相同的对象会导致缓存命中;那些插入的对象仍然在缓存中,因此返回缓存版本。

鉴于此,很明显为什么Ticks不匹配。非缓存对象从数据库中提取DateTime值,其中精度定义为仅将时间存储在最近的秒内。缓存的对象具有原始的DateTime.Now值,它将时间存储到ms。这就解释了为什么两个DateTimes之间的Ticks不匹配,即使两个对象代表相同的底层数据库记录。