views:

1115

answers:

3

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? :-)

A: 

If you're using LINQ to Objects, I wouldn't expect to see dynamic methods being created. I'd expect them with LINQ to SQL etc. Could you give an example where you're seeing this?

I don't really have any good debugging tips when it comes to LINQ, but I'm pretty sure MS know about this as a pain point. Could I suggest you try the VS2010 CTP and see if that's better? More for the sake of improving VS than for solving your immediate problem, admittedly.

Jon Skeet
By LINQ to Objects I mean using in-memory collections via AsQueryable()
Orlangur
So why AsQueryable? *generally* IEnumerable<T> is simpler, and it means that simple lambdas can get used directly. You can still .Compile() your manually built lambdas to use them...
Marc Gravell
(ah right, you answered that on my post)
Marc Gravell
+2  A: 

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;
            }
        }
    }
}
Marc Gravell
Unfortunately actual methods are not an option in our case since the code that builds the expressions is used with other LINQ providers too so it has to work with IQueryable
Orlangur
Fair enough [thinks...]
Marc Gravell
Well, I guess I can create my own LINQ-to-objects provider specifically for debugging that will evaluate all the expressions step-by-step recursively and will replace queryable method calls with enumerable method calls. That should solve the problem. Not an easy task itself however.
Orlangur
Indeed. Sorry, I can't think of anything better, except to manually break your expressions down into small pieces when unit testing... not always easy.
Marc Gravell
A: 

Take a look at the debug visualizer for dynamic methods originally developed by Haibo Luo and taken further by Roy Osherove

VirtualStaticVoid