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.
精彩评论