gpt4 book ai didi

c# - 返回具有匿名类型的已过滤 IQueryable

转载 作者:行者123 更新时间:2023-12-01 20:21:58 25 4
gpt4 key购买 nike

我有一组报告,需要在返回输出之前对其执行过滤。我想使用单个匿名方法来执行此操作,以避免在不同的存储库中重复相同的代码。我正在使用 Entity Framework ,因此模型类型都与数据库相关,并继承自名为 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> delegate 传入过滤选项 this 但Where 操作似乎想要返回一个列表。

是否有一种方法可以达到我想要的效果?

最佳答案

  1. 声明一个具有执行此操作所需的两个 ID 属性的接口(interface)。
  2. 确保您的实体实现该接口(interface)。
  3. 向通用参数添加约束,使其实现该接口(interface)。

请注意,如果您的基类定义了两个相关属性,那么您不需要接口(interface),并且可以简单地将类型限制为该基类:

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 进行比较的谓词,对于表达式来说比对于常规委托(delegate)要复杂一些。这里我们真正需要的是一个Compose方法;对于委托(delegate)来说,这很简单,要将一个方法与另一个方法组合,您只需使用第一个方法的结果来调用它。对于表达式,这意味着获取一个表达式的主体并将参数的所有实例替换为另一个表达式的主体,然后将整个内容包装在一个新的 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;

关于c# - 返回具有匿名类型的已过滤 IQueryable<T>,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26809701/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com