LINQ to Entities Sort Expression based on client input
How can I dynamically sort an Entity Framework query based on a value provided by client?
I have 2 user inputs: one that is value to filter the project by and the other is the way to order the results - either by date, state, priority or type.
The data is displayed in a Grid.
I have one query like this:
Expression<Func<Issue, object>> sortExpression = IssuesConversionsFilter.Convert(sortBy);
Requests = query.Where(i => i.Project.ProjectID == projectId && i.Project.Enabled)
.OrderByDescending(sortExpression)
.Skip(( currentPage - 1 )*take)
.Take(take)
IssuesConversionFil开发者_如何学Goter is a static class with a cache keyed by an enum and with a value that is an Expression<Func<Issue, object>>
:
internal static class IssuesConversionsFilter
{
readonly static IDictionary<IssuesSortBy, Expression<Func<Issue, object>>> Cache = new Dictionary<IssuesSortBy, Expression<Func<Issue, object>>>();
static IssuesConversionsFilter() {
Cache.Add(IssuesSortBy.Date, i => i.CreatedDate);
Cache.Add(IssuesSortBy.Priority, i => i.Priority);
Cache.Add(IssuesSortBy.Type, i => i.Type);
Cache.Add(IssuesSortBy.State, i => i.State);
}
public static Expression<Func<Issue, object>> Convert(IssuesSortBy sortBy) {
if(Cache.ContainsKey(sortBy) == false)
throw new InvalidOperationException();
return Cache[sortBy];
}
}
The problem that the return type has to be an expression that returns an object
and LINQ to Entities only seems to support primitive types. With LING-to-Objects this works fine.
How can I get this to work?
Instead of trying to get the helper class to be able to return a generic expression that can be used in the OrderBy(Descending) method call (which you've found is difficult when the data types aren't all the same), it will be much easier to just perform the call to OrderBy(Descending) in the helper method. For example:
internal static class IssuesConversionsFilter
{
public static IOrderedQueryable<Issue> Convert(IQueryable<Issue> query, IssuesSortBy sortBy)
{
switch (sortBy)
{
case IssuesSortBy.Date:
return query.OrderByDescending(i => i.CreatedDate);
case IssuesSortBy.Priority:
return query.OrderByDescending(i => i.Priority);
case IssuesSortBy.Type:
return query.OrderByDescending(i => i.Type);
case IssuesSortBy.State:
return query.OrderByDescending(i => i.State);
default:
throw new ArgumentOutOfRangeException("sortBy");
}
}
}
Then you can use that method like this:
var orderedQuery = IssuesConversionsFilter.Convert(unOrderedQuery, IssuesSortBy.Date);
You could also change the signature of the Convert method to include this
:
public static IOrderedQueryable<Issue> Convert(this IQueryable<Issue> query, IssuesSortBy sortBy)
and then you'd be able to use it as an extension method. That way, you'll maintain the fluid calling style from your example:
var Requests = query.Where(i => i.Project.ProjectID == projectId && i.Project.Enabled)
.Convert(sortBy)
.Skip(( currentPage - 1 )*take)
.Take(take)
精彩评论