我有一个方法可以通过反射通过属性对可查询的对象进行排序,如下所示:
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);
}
如果我将其用于对象的属性或其子级属性,它将起作用,但是当子级属性是List时,它将不起作用。
// 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()
);
现在,我只想让列表上的第一级孩子工作,也就是说,我不关心级联列表的情况,链中只有一个孩子是列表。
到目前为止,我的尝试都失败了。我正在检查last属性是否是一个集合,如果是,我尝试为该列表创建一个lambda,为该类的元素目标订单属性创建另一个lambda。
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
));
}
我对反射的知识有限,是否可以通过反射来实现?
答案 0 :(得分:1)
一个选择是在父类上放置一个新属性,并使它负责返回所需的值。这样的;
public class User
{
public string Name { get; set; }
public Address CurrentAddress { get; set; }
public List<Phone> PhoneLines { get; set; }
private DateTime _dummyValue;
public DateTime GetRenewalDate
{
get
{
if (this.PhoneLines == null || this.PhoneLines.Count == 0)
return DateTime.MinValue;
else
{
return this.PhoneLines.OrderByDescending(y => y.RenewalDate).Select(y => y.RenewalDate).FirstOrDefault();
}
}
set
{
_dummyValue = value;
}
}
}
确保您的列表为空或为null时使用默认处理,这里我返回DateTime的MinValue。
编辑: 为了解决其他问题,您已经在评论中添加了
LINQ中不支持指定的类型成员'GetRenewalDate' 实体。仅初始化器,实体成员和实体导航 属性。
您可以尝试添加一个虚拟集,这也可以作为一种解决方法(但不是很优雅)。 我已经编辑了上面的代码以显示此内容