user2604373
user2604373

Reputation: 25

OrderBy selector fails while projecting anonymous type

When using Entity Framework 5, why would this work?

var query = Categories.Select(c => new 
    { 
        Products = c.Products.OrderBy(p => p.Name) 
    });

While this won't?

Func<Product, string> selector = p => p.Name;
var query = Categories.Select(c => new 
    { 
        Products = c.Products.OrderBy(selector) 
    });

The thrown exception is: Unsupported overload used for query operator 'OrderBy'.

Upvotes: 2

Views: 441

Answers (3)

Slauma
Slauma

Reputation: 177163

Making selector an Expression<Func<Product, string>> won't work directly and will not compile because c.Products is not an IQueryable<T>. It is just a collection type implementing only IEnumerable<T>. Enumerable.OrderBy does not accept an expression as parameter, only a delegate.

But EF still needs an expression, not a delegate. The trick is to use AsQueryable() on the navigation collection:

Expression<Func<Product, string>> selector = p => p.Name;
var query = Categories.Select(c => new 
{ 
    Products = c.Products.AsQueryable().OrderBy(selector) 
});

Upvotes: 0

Dan Csharpster
Dan Csharpster

Reputation: 2732

Here is a working example of the query working okay and not causing a problem. I skipped the database for simplicity and went directly to an in-memory object, which is probably why it's working for me. I think Honza is right, that this is an issue related to the ORM layer. You can run the below example

Here is an example for linqpad:

void Main()
{

        var Categories = new List<Category>() { new Category { CategoryName = "CatName", Products = new List<Product>() { new Product { Name = "ProductName1" } } } };

        Func<Product, string> selector = p => p.Name;
        var sb = new StringBuilder();
        var query = Categories.Select(c => new
        {
            Products = c.Products.OrderBy(selector)
        });
        foreach (var x in query)
        {
            sb.AppendLine(x.Products.First().Name);
        }

        Console.WriteLine(sb.ToString());
        Console.Read();


    }


    public class Product
    {
        public string Name { get; set; }
    }

    public class Category
    {
        public string CategoryName { get; set; }
        public List<Product> Products { get; set; }
    }

// Define other methods and classes here

Here is a version for visual studio console app:

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            var Categories = new List<Category>() { new Category { CategoryName = "CatName", Products = new List<Product>() { new Product { Name = "ProductName1" } } } };
        Func<Product, string> selector = p => p.Name;
        var sb = new StringBuilder();
        var query = Categories.Select(c => new
        {
            Products = c.Products.OrderBy(selector)
        });
        foreach (var x in query)
        {
            sb.AppendLine(x.Products.First().Name);
        }

        Console.WriteLine(sb.ToString());
        Console.Read();


    }


    public class Product
    {
        public string Name { get; set; }
    }

    public class Category
    {
        public string CategoryName { get; set; }
        public List<Product> Products { get; set; }
    }
}

}

Upvotes: 0

Honza Brestan
Honza Brestan

Reputation: 10957

The query variable name hints that you may be using Entity Framework, LINQ to SQL or some other IQueryable<T> based API. You are passing the selector as Func. The underlying query provider cannot translate Func to SQL (or any other language, whatever you may be using).

Change the type of selector to Expression<Func<Product, string>> (the rest can remain the same, because lambda expressions can be interpreted either as a delegate or an expression tree. That's why you can's use var with lambdas - the compiler just can't tell if you want the lambda to be a delegate or expression tree) and see if that fixes your problem. You haven't provided enough information for me to be 100% sure, but it should. The OrderBy overloads accepting an Expression should be able to walk the expression tree and translate it to the underlying query. It's somewhat a guess, but I hope it helps you.

Upvotes: 6

Related Questions