c# - 将属性访问表达式转换为谓词表达式

标签 c# .net-core entity-framework-core .net-core-3.0

我正在尝试创建一个通用过滤类,它将接收属性访问器并检查它是否在允许的值范围内。所以过滤器的签名是:

class PropertyFilter<TItem, TProperty>
{
    PropertyFilter(Expression<Func<TItem, TProperty>> accessor, IEnumerable<TProperty> allowedValues);

    IQueryable<TItem> Filter(IQueryable<TItem> items);
}

和使用

var filter = new PropertyFilter<Entity, string>(e => e.Name, new [] { "NameA", "NameB" });

await filter.Filter(dbContext.Entities).ToListAsync();

事情必须是 IQueryable兼容,所以我需要编写一个表达式。来自 x => x.Property 形式的表达式我需要创建一个表达式 Expression<Func<TItem, bool>>相当于x => allowedValues.Contains(x.Property) 。从我所看到的来看,我基本上可以使用访问者等对表达式执行任何操作,但我不知道将表达式转换为 SQL 的规则是什么,以及我不能做什么或破坏它,而且该用例似乎太简单了,无法保证需要那么多代码来实现我自己的访问者并测试所有这些。有没有一种简单的方法可以做到这一点,或者可能有一个可靠的库已经解决了这个问题并且与 .NET Core 3.0 和 EF Core 3.0 预览版兼容?

最佳答案

首先,EF Core 3.0 预览版不像任何预览版(测试版)软件那样可靠。此外,目前他们正在重写 LINQ 查询表达式树翻译,因此它非常不稳定,很多东西都不起作用,即使它们在最后一个稳定的 EF Core 2.x 中工作。

因此,此时尝试解决这些问题是没有意义的。最好保留最后一个稳定的 2.x 并等待官方 3.0 发布。

无论如何,你所要求的叫做表达合成,并且有很多帖子如何做到这一点,包括我写的一些。重要的是使用参数替换技术而不是Expression.Invoke,因为后者在3.0之前的版本中工作,但众所周知前者可以与所有查询提供程序一起工作。

所以你需要一个像这样的小型辅助表达式实用程序

public static partial class ExpressionUtils
{
    public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
        => new ParameterReplacer { source = source, target = target }.Visit(expression);

    class ParameterReplacer : ExpressionVisitor
    {
        public ParameterExpression source;
        public Expression target;
        protected override Expression VisitParameter(ParameterExpression node)
            => node == source ? target : node;
    }
}

然后是一个辅助表达式的编写方法,如下所示:

public static partial class ExpressionUtils
{
    public static Expression<Func<TOuter, TResult>> Select<TOuter, TInner, TResult>(
        this Expression<Func<TOuter, TInner>> innerSelector,
        Expression<Func<TInner, TResult>> resultSelector)
        => Expression.Lambda<Func<TOuter, TResult>>(
            resultSelector.Body.ReplaceParameter(resultSelector.Parameters[0], innerSelector.Body),
            innerSelector.Parameters);
}

有了这些帮助器,相关表达式的实现(幸运的是在 3.0 预览版中有效)将变得简单:

return accessor.Select(value => allowedValues.Contains(value));

以这种方式编写表达式的好处是,结果与编译时的结果完全相同,因此获得支持的机会更大。

关于c# - 将属性访问表达式转换为谓词表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57311417/

相关文章:

c# - 如何从另一个子窗体关闭子窗体而不关闭主窗体c#

c# - 新的 Outlook 邮件消息不加载

c# - 实时遥测不适用于 Azure Function .NET Core 6

c# - 对于使用 .NET Core Version 2+ 创建的软件,需要转到 “live” ,是否有任何理由在目标 “live” 操作系统上安装 .NET Core 运行时?

c# - 更新 EFCore 连接的通用方法

asp.net-core - 如何在EF Core 3.0中使用FromSqlInterpolated对多个参数执行存储过程?

c# - WPF 列表框项目消耗太多内存

C# MVC ViewModel 返回 Null - 回传

msbuild - 如何使 .NET Core MSBuild 不为元包项目生成空程序集

c# - 如何将大型标志枚举存储到 SQL 数据库中的单个列?