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

SDw*_*rfs 8 datetime entity-framework compare sql-server-2008 entity-framework-5

关于DateTime值的比较,我在C#下的实体框架(Code First)遇到了问题.我使用下面定义的类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; }
}
Run Code Online (Sandbox Code Playgroud)

在最后三行,您将找到我们将作为示例的"Item"类.我们来看看这个查询:

DateTime requestTime = DateTime.Now;
var items = from n in Validity.isValidAt(db.Items, requestTime)
            select n;
Run Code Online (Sandbox Code Playgroud)

此查询应仅返回类Item的返回对象,这些对象在"requestTime"处"有效".请注意,对于ValidTo == requestTime,项目将被视为"无效"(时间段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 ...

}
Run Code Online (Sandbox Code Playgroud)

**注意:此错误很少发生,但几乎所有时间都在软件中发生.inValidate(requestTime); 通常被称为使对象无效.**

我使用LinQ生成的SQL查询通过Microsoft SQL Server Management Studio手动检查(Microsoft SQL Server 2008用作后端).我必须自己声明/设置@ 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'
Run Code Online (Sandbox Code Playgroud)

这实际上按预期工作.但如果我使用'2012-10-23 15:15:11'作为价值,我会得到错误的结果(如预期的那样).它们与我的程序中的类似.所以我猜这就是问题所在......

在数据库中,"DateTime"定义了毫秒,并且存储了ValidFrom/ValidTo,包括毫秒.但我假设查询不包括任何原因的时间戳的毫秒部分......变量requestTime如何设置毫秒值.

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

我尝试过:1.db.Log = Console.Out;由于"db.Log"未定义的错误而无法编译(也是自动完成没有建议"Log").而db是从DbContext派生的.2.同时将"items"转换为ObjectQuery,然后使用.ToTraceString()不起作用,程序在运行时崩溃,错误消息表明转换无效.

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

问题

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

最好的祝福,

斯特凡

编辑(更多细节)

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

我尝试了以下代码来更改数据库中的数据类型.正如Slauma所建议的那样(参见/sf/answers/563101731/).

modelBuilder.Entity<Item>().Property(f => f.ValidFrom)
  .HasColumnType("datetime2").HasPrecision(3);
modelBuilder.Entity<Item>().Property(f => f.ValidTo)
  .HasColumnType("datetime2").HasPrecision(3);
Run Code Online (Sandbox Code Playgroud)

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

结果:使用HasPrecision(x)没有成功; 其中x是0,3之一; (有或没有db.SaveChanges()之前); 但是:x = 7与db.SaveChanges()相当适用; 直接在查询之前......

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

目前的解决方法

我将以下方法应用于任何DateTime值,然后将其分配给数据库对象属性.它只是将DateTime四舍五入到全秒精度(我在DB上配置).这也适用于任何用于比较的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;
    }
Run Code Online (Sandbox Code Playgroud)

Ste*_*rka 0

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

我不知道有其他方法来提取执行的命令。