C# 对象函数在LINQ到实体表达式中失败

C# 对象函数在LINQ到实体表达式中失败,c#,sql,linq,linq-to-entities,C#,Sql,Linq,Linq To Entities,请注意:我知道如何解决这个问题。我不是在寻找解决方案,我是在寻找问题本身的清晰性 class Program { static void Main(string[] args) { using (var context = new TestDbContext()) { var eventsFound = context.Events .Where(e =>

请注意:我知道如何解决这个问题。我不是在寻找解决方案,我是在寻找问题本身的清晰性

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; }
}
为什么LINQ不能区分数据库函数和对象函数之间的区别。系统应该足够聪明,能够认识到AddDays函数是DateTime对象的一部分。然后,它应该首先解析该函数,然后在解析完查询中的所有函数后,将其转换为SQL并对数据库执行

我相信这要复杂得多,但我想知道为什么

==========编辑==============

因此,上面的例子实际上不是一个好例子,因为“AddDays”是一个在.NET和SQL中都存在的函数。如果我把它改成一个自定义函数,在这个函数中不存在歧义,那会怎么样呢

即:


如果DateTime对象不明确,则替换为string/int对象。

问题是EF正在尝试转换,然后在SQL端执行查询。这里面没有等价物

因此,
DateTime.AddDays
方法不是规范函数或数据库函数,无法转换为适当的命令树节点以供进一步执行。通常,您应该在查询中使用SqlFunctionsEntityFunctions。但是仍然有一种方法可以通过在.edmx文件中定义自定义数据库函数来调用它们。

还要考虑到LINQ to Entities不支持一些标准查询方法:
聚合
最后一个
,等等,以及许多重载,如
选择(IQueryable,Expression)

支持的运算符的完整列表为


要解决此问题,您应该使用:
DateTime.Now.AddDays(+1)
替换为
EntityFunctions.AddDays(DateTime.Now,1)

您也可以将这些天存储在变量中:

DateTime yesterday = DateTime.Now.AddDays(-1);
DateTime tomorrow = DateTime.Now.AddDays(+1);
并且在其中:

.Where(e => 
    e.EventDate >= yesterday  && 
    e.EventDate <= tomorrow 
)
。其中(e=>
e、 EventDate>=昨天&&

e、 EventDate这与它的“聪明”无关,更多的是与Linq的工作方式有关。Linq使用一种称为“表达式树”的东西。基本上,它将表达式编译成一组数据,然后由转换层转换成SQL

这不起作用的原因是因为这是在where子句中,where子句必须在SQL中执行才能准确。它不能在后端的C#代码中执行,至少不能在不静默返回表的所有行的情况下执行,这不是所需的功能……如果是,您可以告诉它显式执行

实体框架提供了一组函数,用于处理可以直接转换为SQL的日期,这些函数位于EntityFunctions命名空间中。这些函数映射到所谓的“规范函数”这只意味着有1:1的SQL转换。Linq to SQL将客户端求值where子句作为参数传递,但这可能是也可能不是所需的值,因为您可能需要服务器端值而不是客户端计算值。因此,L2S在某些情况下会给您意外的结果

简单地说,您需要特殊的表达式函数才能转换为SQL,而任何旧的标准.NET类都无法工作,不幸的是,DateTime类就是这样

您可能会发现以下文章很有用:


如果我们直接使用
DateTime,我们会注意到LINQ to SQL和Entity Framework生成的不同查询

LINQ到SQL:

WHERE ([t0].[EventDate] >= @p0) AND ([t0].[EventDate] <= @p1)

其中([t0].[EventDate]>=@p0)和([t0].[EventDate]=CAST(SysDateTime()作为datetime2))和([Extent1].[EventDate]我很感激您需要一个解释,但作为一个临时修复,您可以在函数的开头创建两个DateTime变量。我不了解linq的细节,但我认为与两个局部变量进行比较要比让它为列表中的每个对象计算函数更快(如果它是这样做的话)。我只是想说清楚,我知道如何解决这个问题。我不是在寻找解决方案,我是在寻找问题本身的清晰性。请注意,在LINQ to SQL下,它工作正常,因此您应该删除LINQ to SQL标记。哎呀,我没有看。它已被删除。LINQ to SQL处理这一问题更加优雅,它将值包装在where into中参数并将其传递到SQL中,然后生成如下SQL
([t0].[EventDate]>=@p0)和([t0].[EventDate]当LINQ to SQL支持“开箱即用”时,Entity Framework在5个版本之后仍然无法实现像这样简单的功能,这让我感到失望。@DoctorJones-L2S会在服务器端默默地使用客户端值,这让我感到失望。EF并不是故意这样做的,因为这样做会让我屈服正确的结果。@MystereMan如果EF对
日期时间不做任何处理。现在
(它在查询中使用CLR值,而不是SQL server上的“当前日期和时间”),调用
.AddDays(n)是非常愚蠢的
对同一个值执行时,会导致它失败,而不是将参数解析为修改后的值。因此,这不是您希望代码在何处执行的问题,这反过来又使对EF的批评完全有效。这在LINQ to SQL中正常工作,并且还使用表达式树。它足够智能,可以知道函数何时执行调用将返回一个原语,并将结果包装成一个参数传递到SQL中。这在实体框架中也是可能的,只是开发团队做出了不实现此行为的设计决策。@DoctorJones-错,它不能正常工作。因为Linq to SQL对客户端上的DateTime函数求值,所以t表示传递的值是客户端值,而不是服务器值。
.Where(e => 
    e.EventDate >= yesterday  && 
    e.EventDate <= tomorrow 
)
WHERE ([t0].[EventDate] >= @p0) AND ([t0].[EventDate] <= @p1)
WHERE ([Extent1].[EventDate] >= CAST( SysDateTime() AS datetime2)) AND ([Extent1].[EventDate] <= CAST( SysDateTime() AS datetime2))