簡體   English   中英

追蹤Linq表達式評估

[英]Tracing Linq expression evaluation

我想知道是否有可能為IQueryable編寫一個“passthrough”擴展方法,它會在評估可查詢時編寫調試字符串,換句話說,調試打印應該是評估的副作用。

就像是:

var qr = SomeSource.Where(...).OrderBy(...).Trace("SomeSource evaluated at {0}", DateTime.Now)
var qr2 = qr.Where(...);

當我構造一個linq查詢並將其作為數據源傳遞給某個對象時,我想知道該對象何時以及多久評估一次我的查詢。 我想它可以通過其他方式實現,例如包裝IEnumerable.GetEnumerator,但我想對任何linq查詢一般這樣做。

我做了類似的事情,但更復雜(因為它在處理表達時也操縱表達式)。 為了實現它,我創建了一個實現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

然后通過提供包裝的可查詢來擴展派生的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

定義一個新的擴展方法:

    public static IEnumerable<T> Trace<T>(this IEnumerable<T> input, 
                                            string format, 
                                            params object[] data) 
    {
        if (input == null)
            throw new ArgumentNullException("input");

        return TraceImpl(input, format, data);
    }

    private static IEnumerable<T> TraceImpl<T>(IEnumerable<T> input, 
                                                string format, 
                                                params object[] data) 
    {
        System.Diagnostics.Trace.WriteLine(string.Format(format, data));

        foreach (T element in input)
            yield return element;
    }

每次迭代時都應該打印一條跟蹤。 感謝Jon Skeet的靈感。

就個人而言,我會用Action委托替換formatdata ,因此您可以執行任何任務(與集合無關),而不僅僅是跟蹤。

編輯:我覺得這可能僅適用於linq到對象。 對於IQueryable<T> ,您必須調整您無權訪問的表達式樹解析器。 對不起: - /

我相信你可以堅持使用LINQ to SQL的標准跟蹤功能。 您不僅可以知道查詢執行的時間,還可以了解查詢,這可以很方便。

為此, DataContext具有Log屬性,允許您跟蹤其SQL輸出。

在LINQ to Entities中, ObjectQuery<T>公開了ToTraceString方法, 您可以使用該方法以您描述的方式進行跟蹤

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM