返回过滤后的IQueryable<;T>;具有匿名类型
本文关键字:类型 gt IQueryable 过滤 lt 返回 | 更新日期: 2023-09-27 18:29:47
我有一组报告,在返回输出之前需要对其进行筛选。我想用一个匿名方法来执行此操作,以避免在不同的存储库中重复相同的代码。我使用的是实体框架,所以模型类型都与数据库相关,并从一个名为ReportBase
的基类继承。
这就是我目前实现过滤的方式,每个报告类型都必须使用不同的上下文来实现此方法,并返回不同的IQueryable类型。
private IQueryable<ReviewAgreement> GetFiltered(ReportFilter filter)
{
IQueryable<ReviewAgreement> reviewAgreementQueryable = Context.ReviewAgreements.Where(p => p.ClientWorkflowId == filter.ClientWorkflowId);
if (filter.AppraisalLevelId.HasValue)
{
reviewAgreementQueryable = reviewAgreementQueryable.Where(p => p.AppraisalLevelId == filter.AppraisalLevelId.Value);
}
return reviewAgreementQueryable;
}
我一直在尝试匿名实现它,这样我就可以重用它,就像这个非功能性的例子一样。
public IQueryable<T> GetFiltered(ReportFilter filter)
{
IQueryable<T> reportQueryable = Context.Set<T>();
reportQueryable = reportQueryable.Where(p => p.ClientWorkflowId == filter.ClientWorkflowId);
if (filter.AppraisalLevelId.HasValue)
{
reportQueryable = reportQueryable.Where(p => p.AppraisalLevelId == filter.AppraisalLevelId.Value);
}
return reportQueryable;
}
当然,我遇到的问题是Where
的使用不明确,因此它无法解决p.ClientWorkflowId
。
我已经尝试使用Func<T, TResult>
委托来传递筛选选项this,但Where操作似乎想要返回一个列表。
真的有什么方法可以达到我想要的效果吗?
- 声明一个具有执行此操作所需的两个ID属性的接口
- 确保您的实体实现该接口
- 向实现该接口的泛型参数添加约束
请注意,如果您的基类定义了这两个有问题的属性,那么您就不需要接口,只需将类型约束到该基类即可:
public IQueryable<T> GetFiltered<T>(ReportFilter filter) where T : ReportBase
{
// body unchanged
}
如果你想接受参数来表示这些属性,那么这也是可能的。首先,您需要接受表达式,而不是Func
对象,以便查询提供程序能够分析它们。这意味着将函数签名更改为:
public IQueryable<T> GetFiltered<T>(ReportFilter filter,
Expression<Func<T, int>> clientIdSelector,
Expression<Func<T, int>> appraisalIdSelector)
{
接下来,将这些选择器转换为谓词,将值与我们所拥有的ID进行比较,这对于表达式来说比对于常规委托来说更为复杂。我们真正需要的是Compose
方法;对于委托来说,这很简单,将一个方法与另一个方法组合,只需调用它,参数是第一个方法的结果。对于表达式,这意味着取一个表达式的主体,将参数的所有实例替换为另一个的主体,然后用一个新的Lambda来封装整个过程。
public static Expression<Func<TFirstParam, TResult>>
Compose<TFirstParam, TIntermediate, TResult>(
this Expression<Func<TFirstParam, TIntermediate>> first,
Expression<Func<TIntermediate, TResult>> second)
{
var param = Expression.Parameter(typeof(TFirstParam), "param");
var newFirst = first.Body.Replace(first.Parameters[0], param);
var newSecond = second.Body.Replace(second.Parameters[0], newFirst);
return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}
这本身取决于用另一个表达式替换一个表达式的所有实例的能力。要做到这一点,我们需要使用以下方法:
public static Expression Replace(this Expression expression,
Expression searchEx, Expression replaceEx)
{
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
internal class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
既然我们已经完成了所有这些,我们实际上可以通过与过滤器的ID值进行比较来组成我们的选择器:
IQueryable<T> reportQueryable = Context.Set<T>();
reportQueryable = reportQueryable
.Where(clientIdSelector.Compose(id => id == filter.ClientWorkflowId));
if (filter.AppraisalLevelId.HasValue)
{
reportQueryable = reportQueryable
.Where(clientIdSelector.Compose(id => id == filter.AppraisalLevelId.Value));
}
return reportQueryable;