5

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

Ihar Bury
  • 485
  • 3
  • 10
  • Linq to objects usually refers to the methods of System.Linq.Enumerable . These do not involve dynamic methods (but can involve anonymous methods). Are you really using dynamic methods? – Amy B Oct 29 '08 at 21:04
  • @David - I asked the same; Orlangur is using AsQueryable to use the same code with multiple sources - so dynamic methods are expected. – Marc Gravell Oct 29 '08 at 21:38

3 Answers3

3

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
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 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 – Ihar Bury Oct 29 '08 at 08:43
  • 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. – Ihar Bury Oct 29 '08 at 19:05
  • 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 Oct 29 '08 at 19:59
1

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
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • By LINQ to Objects I mean using in-memory collections via AsQueryable() – Ihar Bury Oct 29 '08 at 08:41
  • So why AsQueryable? *generally* IEnumerable 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 Oct 29 '08 at 08:43
  • Jon, can you please update the link, I wanted to check out the link but it does not work anymore. Thnx – cpoDesign Aug 06 '12 at 12:49
  • @cpoDesign: Well nearly four years later, we've nearly got a release for VS2012, and VS2010 has been live for a long time... effectively, this answer is no longer useful, and I'll delete it when I've given you a little while to check this comment. – Jon Skeet Aug 06 '12 at 12:55
  • No need to delete it, just have been curious to read what you have suggested. pav – cpoDesign Aug 06 '12 at 13:45
0

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

VirtualStaticVoid
  • 1,656
  • 3
  • 15
  • 21