Tal*_*lon 6 c# sql linq linq-to-entities
请注意:我知道如何解决这个问题.我不是在寻找解决方案,我正在寻找问题本身的清晰度.
class Program
{
static void Main(string[] args)
{
using (var context = new TestDbContext())
{
var eventsFound = context.Events
.Where(e =>
e.EventDate >= DateTime.Now.AddDays(-1) &&
e.EventDate <= DateTime.Now.AddDays(+1)
)
.ToList();
}
}
}
public class TestDbContext : DbContext
{
public DbSet<Event> Events { get; set; }
}
public class Event
{
public int EventId { get; set; }
public DateTime EventDate { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
好的,所以上面的程序失败了:
LINQ to Entities does not recognize the method 'System.DateTime AddDays(Double)'
method, and this method cannot be translated into a store expression.
Run Code Online (Sandbox Code Playgroud)
为什么LINQ不能区分数据库函数和对象函数.系统应该足够聪明,以实现AddDays函数是DateTime对象的一部分.然后它应该首先解析该函数,然后一旦解析了查询中的所有函数,转换为SQL并对数据库执行该函数.
我确定它比这复杂得多,但我想明白为什么.
=========编辑==============
所以上面并不是一个很好的例子,因为"AddDays"是一个存在于.NET和SQL中的函数.当我将其更改为自定义函数时,如果不存在歧义,该怎么办?
即:
public class Event
{
public int EventId { get; set; }
public DateTime EventDate { get; set; }
public DateTime ReturnDateNowExample()
{
return DateTime.Now;
}
}
static void Main(string[] args)
{
var myEvent = new Event {EventDate = new DateTime(2013, 08, 28)};
using (var context = new TestDbContext())
{
var eventsFound = context.Events
.Where(e =>
e.EventDate >= myEvent.ReturnDateNowExample()
)
.ToList();
}
}
Run Code Online (Sandbox Code Playgroud)
如果DateTime对象不明确,则替换为string/int对象.
其原因与"聪明"无关,更多与Linq的工作方式有关.Linq使用了一种叫做"表达式树"的东西.基本上,它将表达式编译为一组数据,然后由转换层转换为SQL.
这不起作用的原因是因为它在where子句中,并且必须在SQL中执行where子句才能准确.它不能在后端的C#代码中执行,至少不能没有静默地返回表的所有行,这不是一个理想的功能......如果是,你可以告诉它明确地这样做.
实体框架提供了一组函数,用于处理可以直接转换为SQL的日期,这些函数位于EntityFunctions命名空间中.这些映射到所谓的"规范函数",这意味着SQL有1:1的转换.Linq to Sql将客户端评估的where子句作为参数传递,但这可能是也可能不是所需的值,因为您可能需要服务器端值而不是客户端计算值...因此L2S会给您带来意外导致某些情况.
简而言之,您需要特殊的表达式函数才能转换为SQL,并且遗憾的是,任何旧的标准.NET类都不起作用,而DateTime类也是如此.
您可能会发现以下文章有用:
http://blogs.msdn.com/b/charlie/archive/2008/01/31/expression-tree-basics.aspx
http://tomasp.net/blog/linq-expand.aspx/
如果我们直接DateTime.Now在查询中使用,请注意LINQ-to-SQL和Entity Framework生成的不同查询:
LINQ到SQL:
WHERE ([t0].[EventDate] >= @p0) AND ([t0].[EventDate] <= @p1)
Run Code Online (Sandbox Code Playgroud)
实体框架
WHERE ([Extent1].[EventDate] >= CAST( SysDateTime() AS datetime2)) AND ([Extent1].[EventDate] <= CAST( SysDateTime() AS datetime2))
Run Code Online (Sandbox Code Playgroud)
这里的区别在于LINQ-to-SQL考虑DateTime.Now必须在.NET端计算并作为查询参数发送的内容,而EF考虑DateTime.Now可以在SQL端计算的内容.从这一点可以清楚地看出,在LINQ-to-SQL中,DateTime.Now.AddDays()"工作"(因为表达式的那一部分完全被评估为.NET端),而在EF上则没有,因为SQL没有AddDays()"工作"完全"作为.NET AddDays()(DATEADD使用整数,而不是浮点).
它是更正确的LINQ-to-SQL做什么或EF做什么?我会说EF更正确(即使它更"奇怪")......
示例:如果.NET应用程序和SQL应用程序位于两个不同的时区(因此具有不同的时间)会发生什么...那么是DateTime.Now.NET时间还是SQL时间更正确?我认为第二个(但我重申,如果我在我的应用程序中发现这是一个"错误",即使我会做一个大的ooooooh).
作为旁注(不是很重要),你不应该在同一个地方计算两次日期,并认为它们是平等的.这次你使用完整的日期,所以没有问题,但如果你只采取了DateTime.Now.Date,如果你的代码是在午夜左右执行,也许,非常非常非常可能两个日期会有所不同,因为一个是在23:59计算的: 59.9999999而另一个是在第二天的00:00:00.0000000计算.