我有以下方法。它返回由我的存储库Get方法
调用的表达式 public Func<IQueryable<Level>, IOrderedQueryable<Level>> GetOrderByExpression()
{
if (request == null)
{
request = new OrderByRequest
{
IsAscending = true, PropertyName = "Name" // CreatedDate , LevelNo etc
};
}
if (string.IsNullOrWhiteSpace(request.PropertyName))
{
request.PropertyName = "Name";
}
Type entityType = typeof(Level);
ParameterExpression parameterExpression = Expression.Parameter(entityType, "x");
PropertyInfo propertyInfo = entityType.GetProperty(request.PropertyName);
Expression<Func<Level, object>> sortExpression =
Expression.Lambda<Func<Level, object>>(
Expression.Convert(Expression.Property(parameterExpression, request.PropertyName),
Type.GetType(propertyInfo.PropertyType.FullName)), parameterExpression);
Func<IQueryable<Level>, IOrderedQueryable<Level>> expression = request.IsAscending
? (Func<IQueryable<Level>, IOrderedQueryable<Level>>)(x => x.OrderBy(sortExpression))
: (x => x.OrderByDescending(sortExpression));
return expression;
}
存储库调用如下(为清晰起见,删除了不必要的代码):
public virtual IQueryable<TEntity> Get(
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null)
{
var query = DbContext.Set<TEntity>().AsQueryable();
if (orderBy != null)
{
query = orderBy(query);
}
}
上述方法适用于Level类的字符串类型的属性。但对于其他类型(如Integer / DateTime等),它不起作用并抛出错误
表达类型&#39; System.Int32&#39;不能用于返回类型 &#39; System.Object的&#39;
我想让这个方法成为一个通用的OrderByExpression提供程序,它将在运行时获取属性名称(此名称将来自客户端),所以 它可以使用该给定对象的任何属性。有可能吗?
答案 0 :(得分:1)
OrderBy
的声明如下
public static IOrderedQueryable<TSource> OrderBy<TSource, TKey>(
this IQueryable<TSource> source,
Expression<Func<TSource, TKey>> keySelector
)
如您所见,还有第二个泛型参数TKey
,当选择器表示属性时,属性的类型。您的代码的问题在于您假设每个属性都可以由Func<TSource, object>
表示,这通常是不正确的。
这是一个通用功能,可以正确地完成您要完成的任务
public static class QueryableUtils
{
public static Func<IQueryable<TSource>, IOrderedQueryable<TSource>> OrderByFunc<TSource>(string propertyName, bool ascending = true)
{
var source = Expression.Parameter(typeof(IQueryable<TSource>), "source");
var item = Expression.Parameter(typeof(TSource), "item");
var member = Expression.Property(item, propertyName);
var selector = Expression.Quote(Expression.Lambda(member, item));
var body = Expression.Call(
typeof(Queryable), ascending ? "OrderBy" : "OrderByDescending",
new Type[] { item.Type, member.Type },
source, selector);
var expr = Expression.Lambda<Func<IQueryable<TSource>, IOrderedQueryable<TSource>>>(body, source);
var func = expr.Compile();
return func;
}
}
在你的情况下使用它将是这样的
public Func<IQueryable<Level>, IOrderedQueryable<Level>> GetOrderByExpression()
{
if (request == null)
{
request = new OrderByRequest
{
IsAscending = true, PropertyName = "Name" // CreatedDate , LevelNo etc
};
}
if (string.IsNullOrWhiteSpace(request.PropertyName))
{
request.PropertyName = "Name";
}
return QueryableUtils.OrderByFunc<Level>(request.PropertyName, request.IsAscending);
}
答案 1 :(得分:0)
我找到了另一种方式。
public Func<IQueryable<Level>, IOrderedQueryable<Level>> GetOrderByFunc()
{
if (request == null)
{
request = new OrderByRequest
{
IsAscending = true,
PropertyName = "Name" // CreatedDate , LevelNo etc
};
}
if (string.IsNullOrWhiteSpace(request.PropertyName))
{
request.PropertyName = "Name";
}
Tuple<Expression, Type> selector = GetSelector(new List<string>() {request.PropertyName});
Type type = selector.Item2;
Type[] argumentTypes = new[] { typeof(Level), type };
var orderByMethod = typeof(Queryable).GetMethods()
.First(method => method.Name == "OrderBy"
&& method.GetParameters().Count() == 2)
.MakeGenericMethod(argumentTypes);
var orderByDescMethod = typeof(Queryable).GetMethods()
.First(method => method.Name == "OrderByDescending"
&& method.GetParameters().Count() == 2)
.MakeGenericMethod(argumentTypes);
if (request.IsAscending)
return query => (IOrderedQueryable<Level>)
orderByMethod.Invoke(null, new object[] {query, selector.Item1});
else
return query => (IOrderedQueryable<Level>)
orderByDescMethod.Invoke(null, new object[] {query, selector.Item1});
}
private static Tuple<Expression, Type> GetSelector(IEnumerable<string> propertyNames)
{
var parameter = Expression.Parameter(typeof(Level));
Expression body = parameter;
foreach (var property in propertyNames)
{
body = Expression.Property(body,
body.Type.GetProperty(property));
}
return Tuple.Create(Expression.Lambda(body, parameter) as Expression
, body.Type);
}