Cannot use a lambda expression as an argument to a dynamically dispatched operation

4.38 (8 votes)

Since the introduction of the Web Pages framework, ASP.NET has been making use of the dynamic type introduced in C# 4.0. Along with that comes some new error messages which at first glance don't make a lot of sense - mainly because they are unexpected. I have already looked at how dynamics do not support extension methods, and a recent question in the ASP.NET forums illustrated another way in which the dynamic type can catch you out.

The full text of the error message was

Cannot use a lambda expression as an argument to a dynamically dispatched operation without first casting it to a delegate or expression tree type 

The code that caused this compilation error looked something like this:

var db = Database.Open("Northwind");
var category = db.QueryValue("SELECT CategoryId FROM Categories WHERE CategoryName = 'Beverages'");
var products = db.Query("SELECT ProductId, ProductName FROM Products WHERE CategoryId = @0", category);
var selectListItems = products.Select(i => new SelectListItem { //error here
    Value = i.ProductId.ToString(), 
    Text = i.ProductName
});

This is illustrative only, so disregard the fact that the two SQL statements can be merged into one.

The error was raised at the point where the questioner attempted to project the results of the second query (variable products) into a new form - an Enumerable<SelectListItem> - in preparation for an HTML DropDown helper. The projection was being attempted through the use of the Enumerable.Select method that takes a delegate represented as a lambda expression. But where is the "dynamically dispatched operation" that doesn't like lambda expressions? The return type from a Database.Query method call is an IEnumerable, not a dynamic type. However, the return type of the Database.QueryValue method is a dynamic. At the moment this isn't clear from the documentation on MSDN, which says that the return type is System.Object. It also inn't clear from WebMatrix, where "IntelliSense" in version 2 is little more than a code completion tool, and doesn't offer any information on compiler errors. However, it is clear from Visual Studio (Express) if you hover over the var keyword:

So how does that affect the products variable? Well, the thing about dynamic is that any operation that it is involved in will be resolved at runtime. In other words, they become dynamic too. And this can be evidenced by hovering over the var keyword relating to the products variable. In other words, the return type of the Database.Query call is no longer an IEnumerable. It's a dynamic:

An interesting thing about dynamic - you can pass one in as an argument to pretty much any method as a parameter. As soon as you do of course, the return type of the method changes to dynamic.

A lambda needs to know the data type of the parameter at compile time. It doesn't like parameters whose type will be resolved at runtime. It needs to be able to infer what type i is in the example above. The advice given in the error message is somewhat typical of .NET error messages that require a ton of research to become clear. However, the solution to the problem is a lot more straightforward. Just as you can pass a dynamic as an argument to any method, you can cast a dynamic to any type. The compiler will happily trust what you are doing and leave it up to the DLR (Dynamic Language Runtime) to complain vigorously if the type you have specified for your dynamic is unnacceptable in the context in which you have tried to use it.

In the case above, you can simply explicitly specify category as an int. Object and string will do just as well in this context, as the value will be passed in to a SQL command as a parameter value - and they are always sent as strings over the wire:

var db = Database.Open("Northwind");
int category = db.QueryValue("SELECT CategoryId FROM Categories WHERE CategoryName = 'Beverages'");
var products = db.Query("SELECT ProductId, ProductName FROM Products WHERE CategoryId = @0", category);
var selectListItems = products.Select(i => new SelectListItem { 
    Value = i.ProductId.ToString(), 
    Text = i.ProductName
});

Or you can opt to force the return type of the Database.Query method to an Enumerable:

var db = Database.Open("Northwind");
var category = db.QueryValue("SELECT CategoryId FROM Categories WHERE CategoryName = 'Beverages'");
IEnumerable<dynamic> products = db.Query("SELECT ProductId, ProductName FROM Products WHERE CategoryId = @0", category);
var selectListItems = products.Select(i => new SelectListItem {
    Value = i.ProductId.ToString(), 
    Text = i.ProductName
});

Either way, the error goes away as the type is now known to the compiler.

So far, I have looked at the WebMatrix data access library, which features a lot of use of the dynamic type. It is also used within the Web Pages framework to provide a storage and access mechanism for the App and Page properties, which allow you to access AppState and PageData entries using dot notation as if they were properties of a class. There are other areas in ASP.NET that make use of dynamics - in particular the ViewBag property that was introduced in MVC 3. This kind of code has already been posted to forums with the same compiler error being reported:

var CountrySelectList = ViewBag.Countries.Select(c => new SelectListItem {
    Value = c.CountryId.ToString(),
    Text = c.CountryName
});

Again, ignoring whether this is fundamentally a good approach, whatever is stored in ViewBag.Countries is a dynamic type. So it needs to be cast to an appropriate type for the operation:

var CountrySelectList = ((IEnumerable<Country>)ViewBag.Countries).Select(c => new SelectListItem {
    Value = c.CountryId.ToString(),
    Text = c.CountryName
});

The dynamic type provides a lot of flexibility and a nice syntax for accessing arbitrary values that might otherwise require dictionaries for storage. But as you have seen, dynamic re-writes some of the rules that you are used to when working with static type checking. So long as you are aware where dynamic is used in Web Pages and MVC, you should be able to avoid the pitfalls.

 

You might also like...

Date Posted:
Last Updated:
Posted by:
Total Views to date: 37774

1 Comment

- angel

I got this same error but in my case there was a typo in the line that declared the @model in my view. Once I removed the extraneous text, the error went away.

Recent Comments

Julian 26/09/2016 14:27
In response to Loading ASP.NET Core MVC Views From A Database Or Other Location
Fantastic, many thanks Mike! Had got half way down this road before finding your article - saved...

Abolfazl Roshanzamir 14/09/2016 05:36
In response to Loading ASP.NET Core MVC Views From A Database Or Other Location
Nice article. Thanke you so much ....

cyrus 02/09/2016 15:12
In response to ASP.NET Web Pages vNext or Razor Pages
I've got some news. As Damian stated in this link: https://github.com/aspnet/Mvc/issues/5208 “We...

Simon 01/09/2016 08:00
In response to Loading ASP.NET Core MVC Views From A Database Or Other Location
Thanks Mike, nice post and exactly what I was looking for. Like you said, I think I'll opt to the...

dave 20/08/2016 14:57
In response to ASP.NET Web Pages vNext or Razor Pages
Do SimplemembershipProvider in viewpages is supported?...

Steven 18/08/2016 04:40
In response to Entity Framework Code First and Stored Procedures
Can you provide the directives (using statements) you're using for EF7 example?...

yousaid 17/08/2016 22:08
In response to ASP.NET Web Pages vNext or Razor Pages
Increasingly, learning a Microsoft tool is no longer worth the return on investment. Too many tools...

jared 12/08/2016 05:54
In response to ASP.NET Web Pages vNext or Razor Pages
hi mike, just for clarification, is viewpages something different from webpages? is webpages still...

Jocke 08/08/2016 20:37
In response to Loading ASP.NET Core MVC Views From A Database Or Other Location
Good post! If this was to be implemented in a CMS where users can change the view files, how would I...

cyrus 05/08/2016 19:49
In response to ASP.NET Web Pages vNext or Razor Pages
I think adding these features to webpages make it complicated. msft forget webpages goal so we move...