从字符串创建表达式(空引用)
问题是:
我们使用表对象来允许用户执行一些功能,如搜索、排序、分页等。这些表效果很好。但其中一项功能存在问题: 排序 (=OrderBy)。
事实上,为了允许排序,我们在每一列中设置一个表示表达式的字符串: 例如,如果表达式为 Person => Person.Id,那么该字符串就是Id; 如果表达式是 Person => Person.Address.Street,字符串为Address.Street。
在第一种情况(Person => Person.Id)中,它工作得很好,因为它不是子对象。 但在第二种情况下(Person => Person.Address.Street),则不会,因为 Address 对象可能为 null。
为了允许从字符串执行 Orderby,我在另一篇文章中发现了以下方法:
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");
}
private static IOrderedQueryable<T> ApplyOrder<T>(IQueryable<T> source, string property, string methodName)
{
string[] props = property.Split('.');
Type type = typeof(T);
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(typeof(T), 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(typeof(T), type)
.Invoke(null, new object[] { source, lambda });
return (IOrderedQueryable<T>)result;
}
你们中的任何人是否有想法允许我添加一个不会选择 subobject == null 的对象的条件?或者阻止它尝试从 null 对象访问属性?
编辑:
检查看起来像:list.OrderBy(x => (x.Address != null) ? x.Address.Street : string.Empty)。
因此,我需要对 x 和最终字段之间的每个对象添加空检查。 使用这些方法可以做到这一点吗?
编辑2:
我尝试替换
Expression.Property(expr, pi);
为
expr = Expression.Condition(
Expression.Equal(expr, Expression.Constant(null)),
Expression.Constant(String.Empty),
Expression.Property(expr, pi));
但似乎不起作用。我得到以下异常:
Argument types do not match
知道我应该如何知道 expr 访问的字段的默认值吗?
Here is the problem:
We use table object to allow the users to perform some functionality like search, sort, pagination etc. Those tables works great. But there is a problem with one of the functionality :
The sort (=OrderBy).
In fact, to allow the sort, we set in every column a string that represent the expression:
For example, if the expression is Person => Person.Id, then the string is Id;
if the expression is Person => Person.Address.Street, the string is Address.Street.
In the first case (Person => Person.Id), it works great since it is not a sub object.
But in the second case (Person => Person.Address.Street), it doesn't since the Address object could be null.
To allow the Orderby to be performed from a string, I found on an other post the following methods :
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");
}
private static IOrderedQueryable<T> ApplyOrder<T>(IQueryable<T> source, string property, string methodName)
{
string[] props = property.Split('.');
Type type = typeof(T);
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(typeof(T), 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(typeof(T), type)
.Invoke(null, new object[] { source, lambda });
return (IOrderedQueryable<T>)result;
}
Does any of you have an idea that would allow me to add a condition that would not select the object with subobject == null ? Or prevent it from Trying to access a property from an object that is null ?
EDIT :
The check would look something like : list.OrderBy(x => (x.Address != null) ? x.Address.Street : string.Empty).
So I need to add a null check on every object between x and the final field.
Is it possible to do that using those methods ?
EDIT 2 :
I've tried to replace
Expression.Property(expr, pi);
by
expr = Expression.Condition(
Expression.Equal(expr, Expression.Constant(null)),
Expression.Constant(String.Empty),
Expression.Property(expr, pi));
But it seems it doesn't work. I get the following exception :
Argument types do not match
Any idea how I'm supposed to know the default value for the field accessed by expr ?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
创建 null 常量作为属性类型。
create your null constant as the property type.