Динамический порядок linq по вложенному свойству с нулевыми свойствами
Я использую эту динамическую функцию упорядочивания linq, которую я получил от здесь.
Это прекрасно работает с вложенными свойствами, поэтому я могу это сделать:
var result = data.OrderBy("SomeProperty.NestedProperty");
Проблема заключается в том, что если SomeProperty имеет значение null, то выполнение OrderBy в NestedProperty бросает печально известную "ссылку на объект, не установленную на экземпляр объекта".
Я предполагаю, что мне нужно настроить следующие строки для обработки исключения:
expr = Expression.Property(expr, pi);
// Or
LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);
Я думал о создании тела оператора, где я мог бы в худшем случае использовать try catch, но это не сработало, поскольку вы не можете иметь тела операторов в orderby выражения linq: "Ярб-выражение с телом оператора не может преобразуется в дерево выражений"
Я потерялся здесь, любые предложения о том, как я могу это сделать?
Кстати, это для Linq для объектов, а не для базы данных.
Ответы
Ответ 1
static void Main(string[] args)
{
var data = new List<MyType>() {
new MyType() { SomeProperty = new Inner() { NestedProperty = "2" }},
new MyType() { SomeProperty = new Inner() { NestedProperty = "1" }},
new MyType() { SomeProperty = new Inner() { NestedProperty = "3" }},
new MyType(),
}.AsQueryable();
var sorted = data.OrderBy(x => GetPropertyValue(x, "SomeProperty.NestedProperty"));
foreach (var myType in sorted)
{
try
{
Console.WriteLine(myType.SomeProperty.NestedProperty);
}
catch (Exception e)
{
Console.WriteLine("Null");
}
}
}
public static object GetPropertyValue(object obj, string propertyName)
{
try
{
foreach (var prop in propertyName.Split('.').Select(s => obj.GetType().GetProperty(s)))
{
obj = prop.GetValue(obj, null);
}
return obj;
}
catch (NullReferenceException)
{
return null;
}
}
Ответ 2
Как насчет дженериков:
Помощник:
public static Expression<Func<TEntity, TResult>> GetExpression<TEntity, TResult>(string prop)
{
var param = Expression.Parameter(typeof(TEntity), "p");
var parts = prop.Split('.');
Expression parent = parts.Aggregate<string, Expression>(param, Expression.Property);
Expression conversion = Expression.Convert(parent, typeof (object));
var tryExpression = Expression.TryCatch(Expression.Block(typeof(object), conversion),
Expression.Catch(typeof(object), Expression.Constant(null)));
return Expression.Lambda<Func<TEntity, TResult>>(tryExpression, param);
}
Иерархия образцов:
public class A
{
public A(B b)
{
B = b;
}
public B B { get; set; }
}
public class B
{
public B(C c)
{
C = c;
}
public C C { get; set; }
}
public class C
{
public C(int id)
{
this.Id = id;
}
public int Id { get; set; }
}
Пример:
var list = new List<B>
{
new B(new A(new C(1))),
new B(new A(new C(2))),
new B(new A(new C(3))),
new B(new A(null)),
new B(null)
}.AsQueryable();
var ordered = list.OrderByDescending(GetExpression<B, Object>("AProp.CProp.Id"));
Вывод:
3
2
1
Null
Null