我已经做了类似的事情,但更复杂(因为它也处理表达式,因为它处理它们)。为了完成它,我创建了一个包装类,它实现了IQueryable并包含对我实际想要查询的事物的引用。我让它将所有接口成员都传递给引用的对象,但Provider属性除外,该属性返回了从IQueryProvider继承的另一个创建的类的引用。 IQueryProvider具有在构造或执行查询时调用的方法。所以你可以做这样的事情,如果你不介意被迫总是查询你的包装对象而不是原始对象。
如果您使用的是LINQ to SQL,还应该注意,DataContext上有一个Log属性,您可以使用它将大量调试信息路由到任意位置。
示例代码:
使自己的IQueryable控制被返回的QueryProvider。
Public Class MyQueryable(Of TableType)
Implements IQueryable(Of TableType)
Private innerQueryable As IQueryable(Of TableType)
Private myProvider As MyQueryProvider = Nothing
Public Sub New(ByVal innerQueryable As IQueryable(Of TableType))
Me.innerQueryable = innerQueryable
End Sub
Public Function GetEnumerator() As System.Collections.Generic.IEnumerator(Of TableType) Implements System.Collections.Generic.IEnumerable(Of TableType).GetEnumerator
Return innerQueryable.GetEnumerator()
End Function
Public Function GetEnumerator1() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
Return innerQueryable.GetEnumerator()
End Function
Public ReadOnly Property ElementType() As System.Type Implements System.Linq.IQueryable.ElementType
Get
Return innerQueryable.ElementType
End Get
End Property
Public ReadOnly Property Expression() As System.Linq.Expressions.Expression Implements System.Linq.IQueryable.Expression
Get
Return innerQueryable.Expression
End Get
End Property
Public ReadOnly Property Provider() As System.Linq.IQueryProvider Implements System.Linq.IQueryable.Provider
Get
If myProvider Is Nothing Then myProvider = New MyQueryProvider(innerQueryable.Provider)
Return myProvider
End Get
End Property
Friend ReadOnly Property innerTable() As System.Data.Linq.ITable
Get
If TypeOf innerQueryable Is System.Data.Linq.ITable Then
Return DirectCast(innerQueryable, System.Data.Linq.ITable)
End If
Throw New InvalidOperationException("Attempted to treat a MyQueryable as a table that is not a table")
End Get
End Property
End Class
使自定义查询提供程序来控制生成的表达式树。
Public Class MyQueryProvider
Implements IQueryProvider
Private innerProvider As IQueryProvider
Public Sub New(ByVal innerProvider As IQueryProvider)
Me.innerProvider = innerProvider
End Sub
Public Function CreateQuery(ByVal expression As System.Linq.Expressions.Expression) As System.Linq.IQueryable Implements System.Linq.IQueryProvider.CreateQuery
Return innerProvider.CreateQuery(expression)
End Function
Public Function CreateQuery1(Of TElement)(ByVal expression As System.Linq.Expressions.Expression) As System.Linq.IQueryable(Of TElement) Implements System.Linq.IQueryProvider.CreateQuery
Dim newQuery = innerProvider.CreateQuery(Of TElement)(ConvertExpression(expression))
If TypeOf newQuery Is IOrderedQueryable(Of TElement) Then
Return New MyOrderedQueryable(Of TElement)(DirectCast(newQuery, IOrderedQueryable(Of TElement)))
Else
Return New MyQueryable(Of TElement)(newQuery)
End If
End Function
Public Shared Function ConvertExpression(ByVal expression As Expression) As Expression
If TypeOf expression Is MethodCallExpression Then
Dim mexp = DirectCast(expression, MethodCallExpression)
Return Expressions.MethodCallExpression.Call(ConvertExpression(mexp.Object), _
mexp.Method, (From row In mexp.Arguments Select ConvertExpression(row)).ToArray())
ElseIf TypeOf expression Is BinaryExpression Then
Dim bexp As BinaryExpression = DirectCast(expression, BinaryExpression)
Dim memberInfo As NestedMember = Nothing
Dim constExp As Expression = Nothing
Dim memberOnLeft As Boolean
Dim doConvert = True
'' [etc... lots of code to generate a manipulated expression tree
ElseIf TypeOf expression Is LambdaExpression Then
Dim lexp = DirectCast(expression, LambdaExpression)
Return LambdaExpression.Lambda(_
ConvertExpression(lexp.Body), (From row In lexp.Parameters Select _
DirectCast(ConvertExpression(row), ParameterExpression)).ToArray())
ElseIf TypeOf expression Is ConditionalExpression Then
Dim cexp = DirectCast(expression, ConditionalExpression)
Return ConditionalExpression.Condition(ConvertExpression(cexp.Test), _
ConvertExpression(cexp.IfTrue), _
ConvertExpression(cexp.IfFalse))
ElseIf TypeOf expression Is InvocationExpression Then
Dim iexp = DirectCast(expression, InvocationExpression)
Return InvocationExpression.Invoke(_
ConvertExpression(iexp.Expression), (From row In iexp.Arguments _
Select ConvertExpression(row)).ToArray())
ElseIf TypeOf expression Is MemberExpression Then
'' [etc... lots of code to generate a manipulated expression tree
ElseIf TypeOf expression Is UnaryExpression Then
'' [etc... lots of code to generate a manipulated expression tree
Else
Return expression
End If
End Function
Public Function Execute(ByVal expression As System.Linq.Expressions.Expression) As Object Implements System.Linq.IQueryProvider.Execute
Return innerProvider.Execute(expression)
End Function
Public Function Execute1(Of TResult)(ByVal expression As System.Linq.Expressions.Expression) As TResult Implements System.Linq.IQueryProvider.Execute
Return innerProvider.Execute(Of TResult)(ConvertExpression(expression))
End Function
End Class
然后通过提供包裹queryables扩展派生的DataContext:
Partial Public Class MyDataContext
Private myQueries As Dictionary(Of System.Type, Object) = New Dictionary(Of System.Type, Object)
Public ReadOnly Property My_AccountCategories() As MyQueryable(Of AccountCategory)
Get
Dim result As Object = Nothing
If (Me.myQueries.TryGetValue(GetType(AccountCategory), result) = false) Then
result = New MyQueryable(Of AccountCategory)(Me.AccountCategories)
Me.myQueries(GetType(AccountCategory)) = result
End If
Return CType(result,MyQueryable(Of AccountCategory))
End Get
End Property
Public ReadOnly Property My_AccountSegmentations() As MyQueryable(Of AccountSegmentation)
Get
Dim result As Object = Nothing
If (Me.myQueries.TryGetValue(GetType(AccountSegmentation), result) = false) Then
result = New MyQueryable(Of AccountSegmentation)(Me.AccountSegmentations)
Me.myQueries(GetType(AccountSegmentation)) = result
End If
Return CType(result,MyQueryable(Of AccountSegmentation))
End Get
End Property
End Class
我希望的注入出头,到最表达式树的一种解决方案,恕我直言,你可以完成同样的事情包装IEnumerable.GetEnumerator,我相信最终会在评估查询时调用它,而且我真的很想避免包装。 – 2011-02-23 23:50:31
注入事物到表达式树中也是我所做的。但我认为除了使用包装器以外,还有其他方法,因为您还需要将代码“注入”到某个地方的阵容中,并且如果没有任何钩子来扩展现有的过程,那么您别无选择但要坚持在前端,以确保您的代码有一定的控制权。 – BlueMonkMN 2011-02-24 13:27:44
这很有道理:)你可以发布一些代码,它不需要清理,只是为了看到基本原理,以便我可以适应它。我最感兴趣的是挖掘提供者。 – 2011-02-24 15:07:44