У меня есть метод, который упорядочивает запрашиваемое свойство через отражение, например:
public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, string orderByProperty, bool desc)
{
string command = desc ? "OrderByDescending" : "OrderBy";
var parts = orderByProperty.Split('.');
var parameter = Expression.Parameter(typeof(TEntity), "p");
Expression body = parameter;
if (parts.Length > 1)
{
for (var i = 0; i < parts.Length - 1; i++)
{
body = Expression.Property(body, parts[i]);
}
}
var property = body.Type.GetProperty(parts.LastOrDefault())
body = Expression.Property(body, property.Name);
var orderByExpression = Expression.Lambda(body, parameter);
resultExpression = Expression.Call(typeof(Queryable), command,
new Type[] { typeof(TEntity), property.PropertyType },
source.Expression, Expression.Quote(orderByExpression));
return source.Provider.CreateQuery<TEntity>(resultExpression);
}
И это работает, если я использую его для свойств объекта или его дочерних свойств, но это не работает, когда дочерним свойством является список.
// Success
db.Users.OrderBy("Name", true)
db.Users.OrderBy("CurrentAddress.City.Name", false)
// Fail
db.Users.OrderBy("PhoneLines.RenewalDate", true)
Моя модель такая:
public class User {
public string Name { get; set; }
public Address CurrentAddress{ get; set; }
public List<Phone> PhoneLines { get; set; }
}
И я пытаюсь сделать что-то вроде этой работы:
db.Users.OrderByDescending(x =>
x.PhoneLines
.OrderByDescending(y => y.RenewalDate)
.Select(y => y.RenewalDate).FirstOrDefault()
);
Прямо сейчас я заинтересован только в том, чтобы эта работа работала для дочерних элементов списка первого уровня, то есть меня не беспокоит сценарий каскадного списка, только один дочерний элемент в цепочке является списком.
Пока мои попытки не увенчались успехом. Я проверяю, является ли последнее свойство коллекцией, и если да, то я попытался создать лямбду для списка и другую лямбду для свойства целевого порядка элемента класса.
if (body.Type.Namespace.Equals("System.Collections.Generic"))
{
var mainType = body.Type.GenericTypeArguments.FirstOrDefault();
var parameterInner = Expression.Parameter(mainType, "x");
var mainProperty = Expression.Property(parameterInner, property.Name);
var orderByExpressionInner = Expression.Lambda(mainProperty, parameterInner);
var orderByExpression = Expression.Lambda(body, parameter);
var selM = typeof(Enumerable).GetMethods()
.Where(x => x.Name == command)
.First().MakeGenericMethod(mainType, property.PropertyType);
var resultExpressionOuter = Expression.Call(typeof(Queryable),
command, new Type[] { typeof(TEntity), body.Type },
source.Expression,
Expression.Call(null, selM, parameterInner, orderByExpressionInner
));
}
Мои знания с рефлексией ограничены, можно ли добиться этого с помощью рефлексии?
'The specified type member 'GetRenewalDate' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.'
22.05.2019