我在C#下遇到有关DateTime值比较的实体框架(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; }
}
在最后三行,你会找到我们将作为例子的“Item”类。我们来看看这个查询:
DateTime requestTime = DateTime.Now;
var items = from n in Validity.isValidAt(db.Items, requestTime)
select n;
此查询应仅返回类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 ...
}
**注意:此错误很少发生,但几乎所有时间都在软件中发生.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'
这实际上按预期工作。但如果我使用'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。
致以最诚挚的问候,
的Stefan
我检查了通过SQL分析器发生了什么,这似乎很好。查询时正确提供具有高(7位)精度的时间戳。但是:我没有得到SELECT导致错误的结果。所以我猜测:它必须是一些缓存。所以我在LINQ查询之前直接放了db.SaveChanges();
。现在我在分析器中收到了所有查询。
我尝试了以下代码来更改数据库中的数据类型。正如Slauma所建议的那样(见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.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;
}
答案 0 :(得分:0)
问题1如何记录/输出完整的SQL(包括占位符的值)? 我认为最好的方法是SQL Server分析器。它显示所有语句和值。 或http://www.hibernatingrhinos.com/products/EFProf
我知道没有办法提取已执行的命令。