C# 返回过滤后的IQueryable<;T>;使用匿名类型

C# 返回过滤后的IQueryable<;T>;使用匿名类型,c#,entity-framework,iqueryable,anonymous-types,C#,Entity Framework,Iqueryable,Anonymous Types,我有一组报告,在返回输出之前需要对其执行过滤。我希望使用一个匿名方法来执行此操作,以避免在不同的存储库中重复相同的代码。我使用的是实体框架,因此模型类型都与数据库相关,并从名为ReportBase的基类继承 这就是我目前实现过滤的方式,每个报表类型都必须使用不同的上下文实现此方法,并返回不同的IQueryable类型 private IQueryable<ReviewAgreement> GetFiltered(ReportFilter filter) { IQueryabl

我有一组报告,在返回输出之前需要对其执行过滤。我希望使用一个匿名方法来执行此操作,以避免在不同的存储库中重复相同的代码。我使用的是实体框架,因此模型类型都与数据库相关,并从名为
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;
}
private IQueryable getfilter(报告过滤器过滤器)
{
IQueryable reviewAgreementQueryable=Context.ReviewAgreements.Where(p=>p.ClientWorkflowId==filter.ClientWorkflowId);
if(filter.assessuallevelid.HasValue)
{
reviewAgreementQueryable=reviewAgreementQueryable.Where(p=>p.evaluvallevelId==filter.evaluvallevelId.Value);
}
返回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;
}
public IQueryable getfilter(报告过滤器过滤器)
{
IQueryable reportQueryable=Context.Set();
reportQueryable=reportQueryable.Where(p=>p.ClientWorkflowId==filter.ClientWorkflowId);
if(filter.assessuallevelid.HasValue)
{
reportQueryable=reportQueryable.Where(p=>p.evaluvallevelid==filter.evaluvallevelid.Value);
}
可查询的返回报告;
}
当然,我遇到的问题是,
在其中的使用是不明确的,因此它无法解决
p.ClientWorkflowId

我曾尝试使用
Func
委托传入筛选选项,但Where操作似乎希望返回列表

有没有一种方法可以达到我想要的效果

  • 声明一个接口,该接口具有执行此操作所需的两个ID属性
  • 确保您的实体实现该接口
  • 向实现该接口的泛型参数添加约束
  • 请注意,如果您的基类同时定义了这两个属性,则不需要接口,只需将类型约束到该基类即可:

    public IQueryable<T> GetFiltered<T>(ReportFilter filter) where T : ReportBase
    {
        // body unchanged
    }
    
    接下来,要将这些选择器转换为谓词,将值与我们拥有的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);
    }
    
    现在我们已经准备好了所有这些,我们实际上可以通过与过滤器的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;
    
    IQueryable reportQueryable=Context.Set();
    reportQueryable=reportQueryable
    .Where(clientDSelector.Compose(id=>id==filter.ClientWorkflowId));
    if(filter.assessuallevelid.HasValue)
    {
    reportQueryable=reportQueryable
    .Where(clientIdSelector.Compose(id=>id==filter.evaluvallevelid.Value));
    }
    可查询的返回报告;
    
    哪里定义了
    ClientWorkflowId
    ?它是在
    ReportBase
    上定义的,还是您的实体都单独定义了
    ClientWorkflowId
    ?那么
    assessuallevelid
    呢?另外,您尝试将筛选器作为
    Func
    传递很可能失败,因为
    Queryable。其中
    需要一个
    表达式
    。我举的示例是一个经过大量修剪的版本,实际上我正在通过许多属性进行筛选。但是是的,它们都是在ReportBase上定义的。所有其他模型都继承了它。难道他不能定义
    其中t:ReportBase
    ?从他的泛型
    GetFiltered
    方法中,我假设所有
    ReportBase
    继承的报告都继承了这两个ID属性。@michaelmoore不清楚情况是否如此,但OP对我的评论的后续行动证实了这一点。因此,是的,如果基类同时定义了这两个属性,OP可以从Servy的答案中跳过#1和#2,直接转到#3,方法是添加
    其中T:ReportBase
    @michaelmoore,当然。我在编辑中澄清了这一点。
    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);
        }
    }
    
    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;