具有毫秒精度的实体框架和 DateTime 比较

Entity Framework and DateTime comparison with millisecond precision

提问人:SDwarfs 提问时间:10/23/2012 最后编辑:CommunitySDwarfs 更新时间:1/23/2013 访问量:5084

问:

我在 C# 下的实体框架(代码优先)中遇到了关于比较 DateTime 值的问题。我使用下面定义的类 Validity (在本例中简化)作为其他实体的超类,这些实体在时间上应具有定义的有效性。

public abstract partial class Validity {
    [Key]
    public int ID { get; set; }

    public DateTime? ValidFrom { get; set; }
    public DateTime? ValidTo { get; set; }

    /**
     * @brief This method builds an IQueryable from another IQueryable,
     * with added restriction on ValidityFrom/To
     *
     * An object's validitiy is defined to
     *   1. start at timestamp ValidFrom (=inclusive) and
     *   2. to end before ValidTo (=exclusive).
     *   3. If ValidFrom or ValidTo is NULL, it means to be "unbounded"
     *      in start or end time (respectively)
     * 
     **/
    public static IQueryable<T> isValidAt<T>(IQueryable<T> query, DateTime time) where T : Validity
    {
        return query.Where<T>(c => 
               (!c.ValidFrom.HasValue || time >= c.ValidFrom)  // If ValidFrom != NULL, the given timestamp must be equal or "after" ValidFrom
            && (!c.ValidTo.HasValue || time < c.ValidTo));     // If ValidTo != NULL, the given timestamp must be "before" ValidTo
    }

    /**
     * @brief Shall invalidate the object at timestamp time (implicitly sets validTo attribute).
     **/
    public void inValidate(DateTime time)
    {
        ValidTo = time;
    }
}

public class Item : Validity {
    public string property { get; set; }
}

在最后三行,您将找到类“Item”,我们将以它为例。让我们看一下这个查询:

DateTime requestTime = DateTime.Now;
var items = from n in Validity.isValidAt(db.Items, requestTime)
            select n;

此查询应仅返回在“requestTime”处“有效”的 Item 类的 Object。请注意,对于 ValidTo == requestTime,Item 将被视为“无效”(ValidFrom 到 ValidTo 的时间跨度是 -exclusive- ValidTo;请参阅上面源代码中的注释)。

问题

我实际上 - 有 - 导致我的结果集“项目”具有 . 我刚刚通过以下方式检查了这一点ValidTo == requestTime

Item i= items.FirstOrDefault();
if ((i.ValidFrom.HasValue && i.ValidFrom > requestTime)
 || (i.ValidTo.HasValue && requestTime >= i.ValidTo)) {

   // ... SOME ERROR OUTPUT ...

}

**注意:此错误并不罕见,但几乎一直在软件中发生,如.inValidate(requestTime);通常被调用以使对象无效。**

我通过 Microsoft SQL Server Management Studio(Microsoft SQL Server 2008 用作后端)使用 LinQ 生成的 SQL 查询手动检查。我必须声明/设置@p__linq__0,@p__linq__1我自己(这两者都意味着 requestTime)......

DECLARE @p__linq__0 DATETIME
DECLARE @p__linq__1 DATETIME
SET @p__linq__0 = '2012-10-23 15:15:11.473'
SET @p__linq__1 = '2012-10-23 15:15:11.473'

这实际上按预期工作。但是,如果我使用“2012-10-23 15:15:11”作为值,我将收到错误的结果(正如预期的那样)。它们与我的程序中的那些相似。所以我想这就是问题所在......

在数据库中,“DateTime”定义了毫秒数,并存储了 ValidFrom/ValidTo,包括毫秒数。但是我假设查询不包含时间戳的毫秒部分,无论出于何种原因......变量 requestTime how ever 设置了毫秒值。

不幸的是,我不知道如何检查查询中发送的实际值来验证这一点。我只知道如何使用 items.toString()-Method 输出生成的 SQL,其中包含占位符。

我试过了: 1.由于错误“db.日志“不会被定义(自动完成也没有建议”日志“)。而 db 派生自 DbContext。 2. 同时将“items”强制转换为 ObjectQuery,然后使用 .ToTraceString() 不起作用,程序在运行时崩溃,并显示强制转换无效的错误消息。db.Log = Console.Out;

如果这很重要:我使用 .NET 4.0 和 EntityFramework.5.0.0。

问题

  1. 如何记录/输出完整的SQL(包括占位符的值)?
  2. 如何以优雅的方式解决这个问题?...我不是说只是从 inValidate() 中分配给“ValidTo”的“时间”中减去一秒的黑客!

此致敬意

斯特凡

编辑(找到更多细节)

我通过 SQL 探查器检查了会发生什么,这似乎很好。查询时,会正确提供高精度(7 位)的时间戳。但是:我没有得到导致错误结果的 SELECT。所以我猜:一定是一些缓存。因此,我直接在 LINQ 查询之前放置了一个。现在我在探查器中获得了所有查询。db.SaveChanges();

我尝试了以下代码来更改数据库中的数据类型。正如斯劳马所建议的那样(见 https://stackoverflow.com/a/8044310/270591)。

modelBuilder.Entity<Item>().Property(f => f.ValidFrom)
  .HasColumnType("datetime2").HasPrecision(3);
modelBuilder.Entity<Item>().Property(f => f.ValidTo)
  .HasColumnType("datetime2").HasPrecision(3);

我在重新启动之前删除了整个数据库...

结果:使用 HasPrecision(x) 未成功;其中 x 是 0, 3 之一;(带或不带 DB。SaveChanges() 直接在之前);但是:x = 7 可以很好地与 db 一起使用。保存更改();直接在查询之前...

所以,不幸的是,这个问题仍然存在......

当前解决方法

在将任何 DateTime 值分配给数据库对象属性之前,我将以下方法应用于该值。它只是将 DateTime 四舍五入为整秒精度(我在数据库中配置了)。此外,这适用于用于比较的任何 DateTime。

结果:这与其说是解决方案,不如说是黑客!我需要为所有 setter 方法编写访问函数,这样就不会意外地发生直接赋值。

    public static DateTime DateTimeDBRound(DateTime time) {
        DateTime t = time;
        long fraction = (t.Ticks % TimeSpan.TicksPerSecond);
        if (fraction >= TimeSpan.TicksPerSecond / 2)
        {
            t = t.AddTicks(TimeSpan.TicksPerSecond - fraction);
        }
        else
        {
            t = t.AddTicks(-fraction);
        }
        return t;
    }
sql-server-2008 实体框架 日期时间 比较 entity-framework-5

评论

0赞 StriplingWarrior 10/23/2012
实体框架的可能重复项丢失了 Sql DateTime 精度
1赞 SDwarfs 10/24/2012
即使提到的文章似乎也是关于同一个问题,它的解决方案不起作用。没有 .edmx 文件。我猜是因为我使用了 Code First 方法。无论如何,我将更详细地研究本文解决方案。如果可以以某种方式解决,我将确认删除请求(或自己删除它)。
1赞 Slauma 10/24/2012
该参数实际上应该以比毫秒高得多的精度传递,即(即 100 皮秒精度): stackoverflow.com/a/11620980/270591 此链接讨论存储 .NET 时精度损失。但是你的结果很奇怪,它不应该发生在数据库中,而是发生在内存中,这对我来说听起来很有问题。您可以尝试使用 as DB 类型(这是 .NET 的确切表示形式)而不是 ,但在我看来,这应该不是必需的。requestTimedatetime2(7)DateTimex<yx>=ydatetime2(7)DateTimedatetime
1赞 Slauma 10/24/2012
如果您不知道如何使用 EF Code-First 将属性映射到列类型:stackoverflow.com/a/8044310/270591DateTimedatetime2(7)
1赞 Pawel 10/24/2012
您是否真的尝试过将 DateTime 的精度设置为 3,如本文中所述?您可以使用 HasPrecision Fluent Api 方法在重写的 OnModelCreating 方法中执行此操作 (msdn.microsoft.com/en-us/library/...)下面是介绍使用 Fluent API 配置模型的链接。msdn.microsoft.com/en-US/data/jj591617

答:

0赞 Steven Spyrka 1/23/2013 #1

问题 1:如何记录/输出完整的 SQL(包括占位符的值)? 我认为最好的方法是使用 SQL Server 探查器。它显示所有语句和值。 或者 http://www.hibernatingrhinos.com/products/EFProf

我不知道没有其他方法可以提取已执行的命令。