We are using LINQ very widely in our system. Particularly LINQ-to-objects. So in some places we end up having a LINQ query in memory build up from some huge expressions. The problem comes when there's some bug in the expressions. So we get NullReferenceException and the stack trace leads us nowhere (to [Lightweight Function]). The exception was thrown inside the dynamic method generated by LINQ.
Is there any easy way to debug such dynamic methods? Or do I have to sacrifice myself to learning WinDBG? :-)
If you are building your own expressions and compiling them, or using AsQueryable, then yes; the LINQ-generated methods will be a royal pain to debug.
You can save some pain by using small fragements of actual methods - at least something useful will show in the stack trace...
Another consideration is: rather than having one huge expression, if you can daisy-chain things a bit more you might have more idea (from the stack trace) where it is failing. The downside is performance - a Where(foo).Where(bar) is two delegate invokes, where-as Where(foo && bar) can be one.
One option might be to swap in a debug version of the extension methods; unfortunately it is a little inconvenient because IQueryable<T>
and Queryable
are in the same namespace... this works, though...
Output first:
>Where: x => ((x % 2) = 0)
<Where: x => ((x % 2) = 0)
>Count
'WindowsFormsApplication2.vshost.exe' (Managed): Loaded 'Anonymously Hosted DynamicMethods Assembly'
<Count
Code:
using System;
using System.Diagnostics;
using System.Linq.Expressions;
namespace Demo
{
using DebugLinq;
static class Program
{
static void Main()
{
var data = System.Linq.Queryable.AsQueryable(new[] { 1, 2, 3, 4, 5 });
data.Where(x => x % 2 == 0).Count();
}
}
}
namespace DebugLinq
{
public static class DebugQueryable
{
public static int Count<T>(this System.Linq.IQueryable<T> source)
{
return Wrap(() => System.Linq.Queryable.Count(source), "Count");
}
public static System.Linq.IQueryable<T> Where<T>(this System.Linq.IQueryable<T> source, Expression<Func<T, bool>> predicate)
{
return Wrap(() => System.Linq.Queryable.Where(source, predicate), "Where: " + predicate);
}
static TResult Wrap<TResult>(Func<TResult> func, string caption)
{
Debug.WriteLine(">" + caption);
try
{
TResult result = func();
Debug.WriteLine("<" + caption);
return result;
}
catch
{
Debug.WriteLine("!" + caption);
throw;
}
}
}
}