Why You Can't Use Extension Methods With A WebGrid

An interesting question came up recently on the ASP.NET forums. In it, the questioner was attempting to use my Chop() extension method to limit the number of characters in a WebGrid column but it wouldn't work. At first, I was a little stumped, so I tried it myself and got an error message:

'string' does not contain a definition for 'Chop'

 

Huh? The code looked fine:

        grid.Column(
            columnName : "Description",
            format: @<text>@item.Description.Chop(20)</text>
            )

..and the extension method was placed in a class called Extensions within App_Code, so it should have worked, surely? Then I studied the error message a little more closely, and saw this:

Exception Details: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'string' does not contain a definition for 'Chop'

The exception was being thrown by the RuntimeBinder, not the Compiler.

The argument that the WebGridColumn's Format parameter takes is a delegate: Func<dynamic,object>. What this means is that you have to pass in a dynamic type, and then something is done to it before it is returned as an object. A dynamic expression is resolved at runtime. The compiler doesn't know what it is.

When you try to use an extension method, the Compiler will check the type you are trying to use it on to see if such a method exists. If it doesn't. it will check any base classes that the type derives from to see if they contain a formal method with the right name. If that test fails, the Compiler will busy itself looking at any referenced static class which contains a static method with a first argument (the this part of an extension method) of the same type that the method is invoked on. In the case of the Chop method, that would a string. With dynamic variables, that reference isn't passed as part of the "Payload" to the RuntimeBinder, so it cannot find the extension method in your App_Code folder. And that, in the shell of a nut, is why you cannot use extension methods on a dynamic.

Great, so what can you do? Well, the extension method is still a static method on a class, so you can call it like this:

        grid.Column(
            columnName : "Description",
            format: @<text>@Extensions.Chop(item.Description, 20)</text>
            )

Note that this issue doesn't just affect items from a query which are passed to the format method in a WebGrid - it affects all the results you get from a database query because the Database.Query method returns an IEnumerable<dynamic>. That means that you cannot simply apply an extension method in this context either:

@foreach(var item in data){
    @item.Description.Chop(30)<br />
}

One thing you can do in the above example (outside of a WebGrid) is to convert the dynamic return to a strongly typed one. You can use Jeremy Skinners Strongly Type helper for WebMatrix.Data for this (http://www.jeremyskinner.co.uk/2011/01/24/strongly-typed-data-access-with-webmatrix-data/).

If you have problems trying to use extension methods within the WebGridColumn's format argument, or anywhere else on the dynamic return from a method, hopefully you know what to do now. But this example also goes to show that it pays to read exception messages carefully.