Return Func<IQueryable<Level>, IOrderedQueryable<Level>> 其中排序依据 属性 在运行时找到
Return Func<IQueryable<Level>, IOrderedQueryable<Level>> where sort by property found in runtime
我有以下方法。它 returns 由我的存储库 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);
}
}
上述方法对于级别 class 的字符串类型属性非常有效。但对于其他类型(如 Integer/DateTime 等),它不起作用并抛出错误
Expression of type 'System.Int32' cannot be used for return type
'System.Object'
我想让这个方法成为一个通用的 OrderByExpression 提供程序,它将在运行时使用 属性 名称(这个名称将来自客户端),所以
它可以与给定对象的任何 属性 一起使用。可能吗?
OrderBy
声明如下
public static IOrderedQueryable<TSource> OrderBy<TSource, TKey>(
this IQueryable<TSource> source,
Expression<Func<TSource, TKey>> keySelector
)
As you see, there is a second generic argument TKey
which when the selector represents a 属性 is the type of the 属性.您的代码的问题在于您假设每个 属性 都可以用 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);
}
我找到了另一种方法。
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);
}
我有以下方法。它 returns 由我的存储库 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);
}
}
上述方法对于级别 class 的字符串类型属性非常有效。但对于其他类型(如 Integer/DateTime 等),它不起作用并抛出错误
Expression of type 'System.Int32' cannot be used for return type 'System.Object'
我想让这个方法成为一个通用的 OrderByExpression 提供程序,它将在运行时使用 属性 名称(这个名称将来自客户端),所以 它可以与给定对象的任何 属性 一起使用。可能吗?
OrderBy
声明如下
public static IOrderedQueryable<TSource> OrderBy<TSource, TKey>(
this IQueryable<TSource> source,
Expression<Func<TSource, TKey>> keySelector
)
As you see, there is a second generic argument TKey
which when the selector represents a 属性 is the type of the 属性.您的代码的问题在于您假设每个 属性 都可以用 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);
}
我找到了另一种方法。
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);
}