C# 跟踪Linq表达式求值
我想知道是否有可能为IQueryable编写一个“passthrough”扩展方法,它将在每次对queryable求值时编写一个debugstring,换句话说,调试打印应该是求值的副作用 比如:C# 跟踪Linq表达式求值,c#,linq,C#,Linq,我想知道是否有可能为IQueryable编写一个“passthrough”扩展方法,它将在每次对queryable求值时编写一个debugstring,换句话说,调试打印应该是求值的副作用 比如: var qr = SomeSource.Where(...).OrderBy(...).Trace("SomeSource evaluated at {0}", DateTime.Now) var qr2 = qr.Where(...); 当我构造linq查询并将其作为数据源传递给某个对象时,我想知
var qr = SomeSource.Where(...).OrderBy(...).Trace("SomeSource evaluated at {0}", DateTime.Now)
var qr2 = qr.Where(...);
当我构造linq查询并将其作为数据源传递给某个对象时,我想知道该对象何时以及多久对我的查询求值一次。我想它可以通过其他方式实现,例如包装IEnumerable.GetEnumerator,但我希望对任何linq查询都能这样做。定义一个新的扩展方法:
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;
}
公共静态IEnumerable跟踪(此IEnumerable输入,
字符串格式,
参数对象[]数据)
{
如果(输入==null)
抛出新的ArgumentNullException(“输入”);
返回TraceImpl(输入、格式、数据);
}
私有静态IEnumerable TraceImpl(IEnumerable输入,
字符串格式,
参数对象[]数据)
{
System.Diagnostics.Trace.WriteLine(string.Format(Format,data));
foreach(输入中的T元素)
收益-收益要素;
}
这应该在每次迭代时打印跟踪。感谢乔恩·斯基特的灵感
就个人而言,我会用操作
委托来替换格式
和数据
,这样您就可以执行任何任务(与集合无关),而不是简单地跟踪
编辑:我觉得这可能只适用于linq to对象。对于
IQueryable
,您必须调整您无权访问的表达式树解析器。抱歉:-/我相信您可以坚持使用LINQ到SQL的标准跟踪功能。您不仅能够知道查询何时执行,而且还可以知道同样方便的查询
为此,DataContext
具有允许您跟踪其SQL输出的属性
在LINQ to Entities中,
ObjectQuery
公开了一个方法,该方法类似,但更复杂(因为它在处理表达式时也会对表达式进行操作)。为了实现它,我创建了一个包装器类,该类实现了IQueryable,并包含了对我实际想要查询的东西的引用。我让它将所有接口成员传递给被引用对象,但Provider属性除外,该属性返回对我创建的另一个类的引用,该类继承自IQueryProvider。IQueryProvider具有在构造或执行查询时调用的方法。因此,如果您不介意被强制总是查询包装器对象而不是原始对象,那么您可以这样做
您还应该注意,如果使用LINQ to SQL,DataContext上有一个日志属性,您可以使用它将大量调试信息路由到任何地方
示例代码:
使您自己的iQuery能够控制返回的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
我希望在表达式树中注入某种解决方案,我想通过包装IEnumerable.GetEnumerator可以完成同样的事情,我相信在计算查询时最终会调用IEnumerable.GetEnumerator,我真的希望避免包装器。将东西注入表达式树也是我所做的。但是我认为除了使用包装器之外没有其他方法,因为您还需要将代码“注入”到某个地方的列表中,如果没有任何钩子来扩展现有过程,你别无选择,只能站在前端,以确保你允许你的代码进行一些控制。这是有道理的:)你可以发布一些代码吗,它不需要清理,只是为了看看基本原则,以便我可以调整它。我最感兴趣的是向供应商挖掘信息。