mal*_*owa 1 c# sorting linq-to-entities entity-framework specification-pattern
我想编写一些API来使用LINQ2Entities对服务器端的实体(SQLServer)进行排序.
我有包含表达式的类表示实体的排序字段和排序方向:
public class SortOption<TEntity>
{
public SortOption(Expression<Func<TEntity, dynamic>> keySelector,
bool ascending = true)
{
KeySelector = keySelector;
Ascending = ascending;
}
public Expression<Func<TEntity, dynamic>> KeySelector { get; private set; }
public bool Ascending { get; private set; }
}
Run Code Online (Sandbox Code Playgroud)
对于我的每个实体,我都有从上面继承的类.例如:
public class PostSorting: SortOption<PostEntity>
{
public PostSorting(): base(p => p.Published)
{
}
}
public class PostEntity
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public DateTime? Published { get; set; }
public DateTime Modified { get; set; }
public string Title { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
主要目标是在我的存储库方法中使用SortOption类的属性,该方法返回实体:
public class Repository<TEntity>
{
public IEnumerable<TEntity> List(SortOption<TEntity> sortOptions)
{
IQueryable<TEntity> query;
if (sortOptions.Ascending)
query = dbSet.OrderBy(sortOptions.KeySelector);
else
query = dbSet.OrderByDescending(sortOptions.KeySelector);
return query;
}
}
Run Code Online (Sandbox Code Playgroud)
*"dbSet"字段是 System.Data.Entity.DbSet<TEntity>
如果我尝试使用PostSorting类通过任何类型与字符串类型不同的属性对实体进行排序,我会得到如下错误:
"LINQ to Entities only supports casting EDM primitive or enumeration types.".
Run Code Online (Sandbox Code Playgroud)
例如(按发布字段排序):
"Unable to cast the type 'System.Nullable`1[[System.DateTime, mscorlib,
Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]' to type
'System.Object'. LINQ to Entities only supports casting EDM primitive or enumeration types."
Run Code Online (Sandbox Code Playgroud)
或(如果我想通过修改字段订购)
"Unable to cast the type 'System.DateTime' to type 'System.Object'.
LINQ to Entities only supports casting EDM primitive or enumeration types."
Run Code Online (Sandbox Code Playgroud)
(如果我想通过Id字段订购)
"Unable to cast the type 'System.Guid' to type 'System.Object'.
LINQ to Entities only supports casting EDM primitive or enumeration types."
Run Code Online (Sandbox Code Playgroud)
我在这项任务上工作了好几天,但我找不到解决问题的答案.
试试这个:
public static class QueryableEx
{
public static IOrderedQueryable<TSource> OrderByEx<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, object>> keySelector)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
if (keySelector == null)
{
throw new ArgumentNullException("keySelector");
}
// While the return type of keySelector is object, the "type" of
// keySelector.Body is the "real" type *or* it is a
// Convert(body). We rebuild a new Expression with this "correct"
// Body (removing the Convert if present). The return type is
// automatically chosen from the type of the keySelector.Body .
Expression body = keySelector.Body;
if (body.NodeType == ExpressionType.Convert)
{
body = ((UnaryExpression)body).Operand;
}
LambdaExpression keySelector2 = Expression.Lambda(body, keySelector.Parameters);
Type tkey = keySelector2.ReturnType;
MethodInfo orderbyMethod = (from x in typeof(Queryable).GetMethods()
where x.Name == "OrderBy"
let parameters = x.GetParameters()
where parameters.Length == 2
let generics = x.GetGenericArguments()
where generics.Length == 2
where parameters[0].ParameterType == typeof(IQueryable<>).MakeGenericType(generics[0]) &&
parameters[1].ParameterType == typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(generics[0], generics[1]))
select x).Single();
return (IOrderedQueryable<TSource>)source.Provider.CreateQuery<TSource>(Expression.Call(null, orderbyMethod.MakeGenericMethod(new Type[]
{
typeof(TSource),
tkey
}), new Expression[]
{
source.Expression,
Expression.Quote(keySelector2)
}));
}
}
Run Code Online (Sandbox Code Playgroud)
你必须写一个OrderByAscending,但它是相同的只是来替换OrderBy用OrderByAscending.该方法重写Expression使用"右"类型.
代码深受Queryable.OrderBy的启发.
| 归档时间: |
|
| 查看次数: |
1115 次 |
| 最近记录: |