我正在为WebGrid编写一个IQueryable友好替代品,我停止了我必须通过作为字符串传递的列名来命令结果的部分。我设法实现了我的目标,但我不喜欢结果代码所以我希望有人可以给我一个提示如何改进它。
我为IQueryable编写了一个扩展名,用于根据我找到的here按字符串排序,但是它最初没有用,并且给了我一个错误,说明(或多或少)Int32无法转换为对象(此时我正在使用一列id来测试列名称的排序,这就是错误说Int32的原因)。有问题的一行:
var mySortExpression = Expression.Lambda<Func<T, object>>(Expression.Property(param, columnName), param);
我添加了一个基于this page可以找到的转换,但是这也没有用,我得到一个错误,说我不能按对象类排序。以下是它如何看待这一点:
var mySortExpression = Expression.Lambda<Func<T, object>>(Expression.Convert(Expression.Property(param, columnName), type), param);
所以我发现我需要在这一行中提供一个可排序的类型:
var mySortExpression = Expression.Lambda<Func<T, type>>(Expression.Property(param, columnName), param);
并将整个事情重写如下:
public static IQueryable<T> OrderByString<T>(this IQueryable<T> query, string columnName)
{
var elementType = typeof(T);
var param = Expression.Parameter(elementType, "x");
var prop = elementType.GetProperty(columnName);
Type type = Nullable.GetUnderlyingType(prop.PropertyType);
if (type == null)
{
type = prop.PropertyType;
}
if (type.Equals(typeof(string)))
{
var mySortExpression = Expression.Lambda<Func<T, string>>(Expression.Property(param, columnName), param);
return query.OrderBy(mySortExpression);
}
if (type.Equals(typeof(char)))
{
var mySortExpression = Expression.Lambda<Func<T, char>>(Expression.Property(param, columnName), param);
return query.OrderBy(mySortExpression);
}
if (type.Equals(typeof(int)))
{
var mySortExpression = Expression.Lambda<Func<T, int>>(Expression.Property(param, columnName), param);
return query.OrderBy(mySortExpression);
}
if (type.Equals(typeof(float)) || type.Equals(typeof(double)))
{
var mySortExpression = Expression.Lambda<Func<T, double>>(Expression.Property(param, columnName), param);
return query.OrderBy(mySortExpression);
}
// This last part won't work but I left it so that it can compile (all routes need to return value etc.)
var mySortExpression1 = Expression.Lambda<Func<T, object>>(Expression.Convert(Expression.Property(param, columnName), type), param);
return query.OrderBy(mySortExpression1);
}
这实际上有效,但所有这些重复看起来都不太好。有没有办法改进这段代码?
答案 0 :(得分:0)
正如RaphaëlAlthaus所建议的那样,我使用https://stackoverflow.com/a/233505/2123652中的代码解决了这个问题,但是我应用了一个小改动来允许它正确处理接口。结果代码如下所示:
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "OrderBy");
}
public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "OrderByDescending");
}
public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "ThenBy");
}
public static IOrderedQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "ThenByDescending");
}
static IOrderedQueryable<T> ApplyOrder<T>(IQueryable<T> source, string property, string methodName) {
string[] props = property.Split('.');
Type elementType = source.ElementType;
Type type = elementType;
ParameterExpression arg = Expression.Parameter(type, "x");
Expression expr = arg;
foreach(string prop in props) {
// use reflection (not ComponentModel) to mirror LINQ
PropertyInfo pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
}
Type delegateType = typeof(Func<,>).MakeGenericType(elementType, type);
LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);
object result = typeof(Queryable).GetMethods().Single(
method => method.Name == methodName
&& method.IsGenericMethodDefinition
&& method.GetGenericArguments().Length == 2
&& method.GetParameters().Length == 2)
.MakeGenericMethod(elementType, type)
.Invoke(null, new object[] {source, lambda});
return (IOrderedQueryable<T>)result;
}
colde的变化涉及用typeof(T)
替换source.ElementType
。