开发者

OrderBy in EF linq to entities can't handle the goodness I'm throwing at it.

I am working on an app that uses Entity Framework 3.5 and an SQL database. The app searches an object graph based on some search criteria passed down from a user interface. In order to handle the search functionality, I'm using a query builder with the following interface:

public interface IQueryBuilder
{
    IQueryable<SomeClass> BuildQuery(Criteria criteria);    
}

The criteria object carries several properties that the query builder uses to filter the search results. It also carries a property that dictates how the results should be sorted, called "OrderBy". Here's an example of the criteria object:

public class Criteria
{
    //some filter properties...

    public Expression<Func<SomeClass, object>> OrderBy { get; set; }
}

So, after the initial query has been built, the query builder sorts the query with:

var sortedQuery = query.OrderBy(criteria.OrderBy);

This works flawlessly in unit tests and integration tests (where it actually hits EF) when I order by a string property (OrderBy = x => something.LastName). BUT, when I run integration tests that order by a non-string pro开发者_如何学JAVAperty (OrderBy = x => something.AgeInYears), EF throw the following exception:

System.NotSupportedException: Unable to cast the type 'System.Int32' to type 'System.Object'. LINQ to Entities only supports casting Entity Data Model primitive types.

In this case, something.AgeInYears is an int. EF doesn't like the fact that I give it an int instead of an object.

SO, is there a way I can do what I'm trying to do here?


I'm guessing you don't want to (for good reason) make your criteria class like

public class Criteria<T, U>
{
    //some filter properties...

    public Expression<Func<T, U>> OrderBy { get; set; }
}

which should work...so here are a couple of alternatives:

public class Criteria<T>
{
    //some filter properties...

    public Expression OrderBy { get; set; }


    public void CreateOrdering<U>(Expression<Func<T, U>> value)
    {
        OrderBy = value;
    }
}

but since OrderBy isn't generic typed anymore, you'd need to alter your code that applies it:

      var orderBy = Expression.Call(
            typeof(Queryable),
            "OrderBy",
            new Type[] { query.ElementType, query.ElementType },
            query.Expression,
            criteria.OrderBy);

      var sortedQuery = query.Provider.CreateQuery<T>(orderBy

alternatively, you could handle this in the code that subsequently applies the ordering:

instead of

var sortedQuery = query.OrderBy(criteria.OrderBy);

do something like (haven't tested this, but should be basically like this) (UPDATE: this one has no pre-supposition that you're using generics).

// grab the property expression out of your ordering statement
var expressionProperty = criteria.OrderBy.Body is UnaryExpression ? (MemberExpression)((UnaryExpression)criteria.OrderBy.Body).Operand : (MemberExpression)criteria.OrderBy.Body;

// recreate the lambda as Expression<Func<T,U>> where T is the queryable element type and U 
// is the actual type of the property, not an object

var orderBy = Expression.Lambda(expressionProperty, criteria.OrderBy.Parameters.Single());

// apply new lambda instead
var sortedQuery = query.OrderBy(orderBy);


Check the methods on the SqlFunctions class which can help do LINQ-safe conversions.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜