重用包含SqlFunctions.DatePart的表达式

时间:2013-05-29 17:31:53

标签: entity-framework expression

我有以下LINQ-to-Entities查询,用于检索与给定日期(dte)的日期部分匹配的记录:

Dim qry = entities.Works.Where(Function(w) w.JobID = RecordID And
  SqlFunctions.DatePart("y", w.FromDate) = SqlFunctions.DatePart("y", dte) And
  SqlFunctions.DatePart("m", w.FromDate) = SqlFunctions.DatePart("m", dte) And 
  SqlFunctions.DatePart("d", w.FromDate) = SqlFunctions.DatePart("d", dte))

我想重用这个表达式,先写下这样的东西:

Public SqlDateEquals As System.Linq.Expressions
    .Expression(Of Func(Of DateTime, DateTime, Boolean)) = Function(dte1, dte2)
     SqlFunctions.DatePart("y", dte1) = SqlFunctions.DatePart("y", dte2) And 
     SqlFunctions.DatePart("m", dte1) = SqlFunctions.DatePart("m", dte2) And 
     SqlFunctions.DatePart("d", dte1) = SqlFunctions.DatePart("d", dte2)

然后,在其他查询中:

Dim qry = entities.Works.Where(Function(w) w.JobID = 
    RecordID And SqlDateEquals(w.FromDate, dte))

我的理解是LINQ-to-Entities解析传递给.Where的表达式,它应该能够将SqlFunctions.DatePart识别为表达式的一部分。因此,包含SqlFunctions.DatePart的表达式仍然可以解析为它们的等效SQL。

我该怎么做?

2 个答案:

答案 0 :(得分:1)

您看到的问题与SqlFunctions.DatePart无关;相反,这是因为你不能以你尝试的方式组合你的两个表达。

像这样编写表达式实际上并不像您期望的那样简单。 lambda表达式语法是一种不支持组合表达式的简写;你必须使用Expression上的静态方法。

幸运的是,对您的案例有一种更简单的方法

更新:使用Expression的静态“构建器”方法添加了完成此操作的示例代码。

Imports System.ComponentModel.DataAnnotations
Imports System.Data.Entity
Imports System.Linq.Expressions
Imports System.Data.Objects.SqlClient
Imports System.Reflection

Class Entity
    <Key> Public Property Key As Integer
    Public Property P As String
End Class

Class Context
    Inherits DbContext

    Public Property Entities As DbSet(Of Entity)

End Class

Module Module1
    Sub Main()
        Dim expression1 As Expression(Of Func(Of Entity, Boolean)) = Function(e) e.P > "m"

        Dim ctx As Context = New Context()
        Dim query = From e In ctx.Entities
                    Where e.P < "p"
                    Select e

        ' Example 1.  The easy way: chaining two Where clauses together.
        ' This works because q.Where(A AND B) is the same as q.Where(A).Where(B)
        Dim q2 = query.Where(expression1)

        Console.WriteLine(q2.ToString())

       ' Now the harder method.
        Dim expression2 As Expression(Of Func(Of Entity, Boolean)) = Function(e) e.P < "p"

        ' Reproduce the above using static builder methods.
        ' Get an expression for our single parameter.
        Dim parameter As ParameterExpression = Expression.Parameter(GetType(Entity))

        ' Get an expression for accessing its P property.
        Dim pProperty = Expression.Property(parameter, "P")

        ' Get a MethodInfo for the string comparison method.
        Dim compareMethod As MethodInfo = (From m In GetType(String).GetMethods()
                                           Where m.Name = "Compare" AndAlso m.GetParameters().Count() = 2 AndAlso m.ReturnType = GetType(Integer)
                                           Select m).First()

        ' Generate our combined lambda.
        Dim combinedExpression = CType(
            Expression.Lambda(
                Expression.Or(
                    Expression.GreaterThan(Expression.Call(compareMethod, pProperty, Expression.Constant("p")), Expression.Constant(0)),
                    Expression.LessThan(Expression.Call(compareMethod, pProperty, Expression.Constant("m")), Expression.Constant(0))),
                parameter), 
            Expression(Of Func(Of Entity, Boolean)))

        Dim combinedQuery = ctx.Entities.Where(combinedExpression)

        Console.WriteLine(combinedQuery.ToString())

        Console.Read()
    End Sub

End Module

上述示例中的第一个表达式的工作原理是.Where(A AND B)在逻辑上与.Where(A).Where(B)相同。如上面的示例代码所示,EF正确地为此生成了很好的SQL代码。

上面代码输出的第二个表达式很多更复杂。它展示了如何在代码中构建表达式。在这里,因为我们进行了OR操作,所以我们无法使用链接Where()调用的技巧。

有关问题及其解决方案的精彩讨论,请参阅this article

答案 1 :(得分:0)

使用EntityFunctions.TruncateTime

Dim datePart = dte.Date
Dim qry = entities.Works.Where(Function(w) w.JobID = RecordID And _
    TruncateTime(w.FromDate) = datePart)

注意:我意识到这不能回答这个问题,但确实解决了这个问题。