在 C# 中组合 BinaryExpression 和 Expression>

2023-12-02

我怎样才能结合BinaryExpression and Expression<Func<dynamic / T, bool>>?

例如:

void AddGlobalFilter<T>(Expression<Func<T, bool>> expr)
{
    var parameter = Expression.Parameter(type, "t");
    var member = Expression.Property(filter.Parameter, field);
    var constant = Expression.Constant(null);
    var body = Expression.Equal(member, constant);

    var combine = Expression.AndAlso(body, expr);
}

我试图定义全局过滤器适用于实体框架 (EF) Core。问题是我必须手动组合多个过滤器.

可以添加一个过滤器ModelBuilder如果模型实现IDbDeleted界面。
可以针对特定型号手动添加另一个。基本想法是我有一个所有表达式的列表,然后将它们组合起来:

var expression = listExpressions.First();
foreach (var second in listExpressions.Skip(1))
{
    expression = Expression.AndAlso(expression, second);
}
var lambdaExpression = Expression.Lambda(expression, parameter);
modelBuilder.Entity(item.Key).HasQueryFilter(lambdaExpression);

当然我得到了错误(第一个来自Expression.Equal第二个来自t => t...):

过滤表达式 't => t => (Not(t. ...

Edited: 代码看起来像这样:

[Table("MyEntities")]
public class DbMyEntity : IDeleted
{
    public string Name { get; set; }
    public DateTime? DateTimeDeleted { get; set; }
}

public interface IDeleted
{
    DateTime? DateTimeDeleted { get; set; }
}

public class MyContext : IdentityDbContext
{
    private Dictionary<Type, List<Expression>> dict = new Dictionary<Type, List<Expression>>();
    private Dictionary<Type, ParameterExpression> dictParameter = new Dictionary<Type, ParameterExpression>();

    private ParameterExpression GetParameter(Type type)
    {
        if (!this.dictParameter.ContainsKey(type))
        {
            this.dictParameter.Add(type, Expression.Parameter(type, "t"));
        }
        return this.dictParameter[type];
    }

    private void AddToDict(Type type, Expression expr)
    {
        if (!this.dict.ContainsKey(type))
        {
            this.dict.Add(type, new List<Expression>());
            this.GetParameter(type);  //Just to create ParameterExpression if not exists.
        }

        this.dict[type].Add(expr);
    }

    private void AddToDict<T>(Expression<Func<T, bool>> expr)
    {
        this.AddToDict(typeof(T), expr);
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {

        base.OnModelCreating(modelBuilder);

        foreach (var entity in modelBuilder.Model.GetEntityTypes())
        {
            if (typeof(IDeleted).IsAssignableFrom(entity.ClrType))
            {
                var member = Expression.Property(this.GetParameter(entity.ClrType), "DateTimeDeleted");
                var constant = Expression.Constant(null);
                var body = Expression.Equal(member, constant);
                this.AddToDict(entity.ClrType, body);
            }
        }

        //This is done in another project in same solution. See comment bellow.
        this.AddToDict<DbMyEntity>(t => t.Name == null || t.Name == "Something");
        //foreach (var builderType in allDllModules)
        //{
        //    if (builderType != null && builderType != typeof(ICustomModelBuilder))
        //    {
        //        var builder = (ICustomModelBuilder)Activator.CreateInstance(builderType);
        //        builder.Build(modelBuilder);
        //    }
        //}

        foreach (var item in this.dict)
        {
            var expression = item.Value.First();
            foreach (var second in item.Value.Skip(1))
            {
                expression = Expression.AndAlso(expression, second);
            }
            var lambdaExpression = Expression.Lambda(expression, this.dictParameter[item.Key]);
            modelBuilder.Entity(item.Key).HasQueryFilter(lambdaExpression);
        }
    }
}

你正在混合表达式 with lambda 表达式。有很多帖子展示了如何组合 lambda 表达式,但最重要的部分是从 lambda 表达式组合表达式bodies并重新绑定参数.

后者通常是通过自定义来实现的ExpressionVisitor像这样:

using System.Linq.Expressions;

public static class ExpressionExtensions
{
    public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
    {
        return new ParameterReplacer { Source = source, Target = target }.Visit(expression);
    }

    class ParameterReplacer : ExpressionVisitor
    {
        public ParameterExpression Source;
        public Expression Target;
        protected override Expression VisitParameter(ParameterExpression node)
        {
            return node == Source ? Target : base.VisitParameter(node);
        }
    }
}

现在介绍 EF Core 组合查询过滤器。

对于您正在做的事情来说,使用字典和表达式列表似乎过于复杂。自从IMutableEntityType提供读/写访问QueryFilter,同样可以通过一小组自定义扩展方法来实现。

他们都进入这样的班级:

using System;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

public static class QueryFilterExtensions
{
}

第一种方法:

public static void AddQueryFilter(this IMutableEntityType target, LambdaExpression filter)
{
    if (target.QueryFilter == null)
        target.QueryFilter = filter;
    else
    {
        var parameter = target.QueryFilter.Parameters[0];
        var left = target.QueryFilter.Body;
        var right = filter.Body.ReplaceParameter(filter.Parameters[0], parameter);
        var body = Expression.AndAlso(left, right);
        target.QueryFilter = Expression.Lambda(body, parameter);
    }
}

这是一种非通用方法,它将现有过滤器与通过的过滤器结合使用AndAlso (C# &&) 运算符并展示了前面提到的 lambda 表达式组合原理。

然而,它并不是那么直接有用,就像在实体类型配置循环中一样(它可以,但需要您手动构建 lambda 表达式,而不是让 C# 编译器这样做)。那么这里就出现了第二种方法:

public static void AddQueryFilter<T>(this IMutableEntityType target, Expression<Func<T, bool>> filter)
{
    LambdaExpression targetFilter = filter;
    if (target.ClrType != typeof(T))
    {
        var parameter = Expression.Parameter(target.ClrType, "e");
        var body = filter.Body.ReplaceParameter(filter.Parameters[0], parameter);
        targetFilter = Expression.Lambda(body, parameter);
    }
    target.AddQueryFilter(targetFilter);
}

它是一个通用方法 - 不太类型安全,但允许您使用编译时 lambda 表达式并将其绑定到实际实体类型,如下所示:

foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
    if (typeof(IDeleted).IsAssignableFrom(entityType.ClrType))
        entityType.AddQueryFilter<IDeleted>(e => e.DateTimeDeleted == null);
}

看起来好多了,不是吗:)

最后一个自定义扩展方法是对标准 EF Core 泛型的补充(替换)HasQueryFilter method:

public static EntityTypeBuilder<TEntity> AddQueryFilter<TEntity>(this EntityTypeBuilder<TEntity> target, Expression<Func<TEntity, bool>> filter)
    where TEntity : class
{
    target.Metadata.AddQueryFilter(filter);
    return target;
}

并允许您替换

this.AddToDict<DbMyEntity>(t => t.Name == null || t.Name == "Something");

与更方便的

modelBuilder.Entity<DbMyEntity>()
    .AddQueryFilter(t => t.Name == null || t.Name == "Something");

更新(EF Core 3.0): QueryFilter属性已替换为GetQueryFilter and SetQueryFilter扩展方法。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 C# 中组合 BinaryExpression 和 Expression> 的相关文章

随机推荐

  • 发送二进制文件 TcpClient - 文件大于源

    为了涉足网络编程 我编写了一个小控制台应用程序来将 png 文件发送到服务器 另一个控制台应用程序 服务器写入的文件比源 png 文件稍大 而且它不会打开 客户端应用程序的代码是 private static void SendFile u
  • 样式不适用于垂直方向的拇指

    我正在创造垂直范围栏我改变了 webkit appearance slider vertical 我还更改了输入范围栏的属性 例如 height width webkit slider runnable track 和 webkit sli
  • LEMP + wordpress 文件权限能够编辑、升级和使用 sftp 客户端

    我正在尝试管理运行nginx的debian网络服务器上的文件权限 以便wordpress可以编辑 上传和升级而无需使用ftp 我还希望能够使用我的用户帐户使用 sftp 登录 我知道这个问题之前已经被问过 请参阅here or here 但
  • 导入 Orange 返回“ImportError:没有名为 Orange 的模块”

    我想使用 Orange 包科学分析 安装于x86 64 Ubuntu 12 04 with Python 2 7 3 进展顺利 使用sudo easy install orange 但是 该包似乎无法直接使用 11 30 43 leon t
  • 使用嵌套 ng-repeat“观察者反应函数不应更改模型”是什么意思?

    这是代码的简化版本 即使生成的 HTML 看起来不错 我也会收到记录的错误 这个异常意味着什么 为什么我会得到它 索引 html div div strong a strong div b div div div
  • 为什么 Oracle 在取消引用对象时需要视图中的列的别名?

    为什么 Oracle 在取消引用对象时需要视图中的列的别名 例如 CREATE VIEW view AS SELECT t eno t workdept dname t salary FROM table t ERROR at line 1
  • 使用 d3-zoom 与 WebGL 交互

    我正在尝试收集一个小示例 该示例使用 d3 zoom 为使用 WebGL 渲染的画布元素提供简单的交互性 我想做的就是提供平移 缩放 这使用 4x4 变换矩阵相当简单 我遇到的问题是缩放 缩放 如果您查看一些 d3 zoom 示例 您会发现
  • 溢出-x 被溢出-y 覆盖[重复]

    这个问题在这里已经有答案了 看起来overflow x and overflow y不要按照我期望的方式行事 如果我设置overflow x可见 并且overflow y到汽车 overflow x不会表现为可见 而是表现为隐藏 我是否错过
  • ASP.NET 应用程序正在显示美国日期格式

    One我的开发应用程序今天开始显示美国格式的短日期 而我期待的是英国格式 日期正在使用date ToShortDateString 我已经检查了我的区域设置 键盘设置 浏览器设置和 web config 这些都设置为英语 英国 或未更改 我
  • C++20 模块“循环依赖”

    我试图将一个模块包含在另一个模块中 但由于以下错误而无法编译 无法构建以下源文件 因为它们之间存在循环依赖性 Module1 ixx 依赖于 Module2 ixx 依赖于 Module1 ixx 我想要 modClass1 包含 modC
  • 适用于 iOS 4.0 应用程序的 NSFileProtectionComplete

    我看到了有关 NSFileProtectionComplete 和保护应用程序数据的 WWDC 2010 视频 有没有什么例子 有人有示例代码可以分享吗 请参阅 NSFileManager 类文档 该文件以加密方式存储 格式化在磁盘上且无法
  • 如何仅解析字符串中的浮点数?

    foreach object item in listBox1 SelectedItems string curItem item ToString var parts curItem Split XY ToCharArray String
  • jline 保持底部提示

    我在用jline我有一个整洁的ConsoleReader一切都很好 但是 如果您在提示符中输入某些内容 并且 stdout 上有输出 来自另一个线程 则输出会拆分您正在输入的单词 命令 我怎样才能保留jline终端底部有提示吗 我在用jli
  • 在构造函数中调用函数时出现 NameError

    我通过调用构造函数中的函数来运行下面的代码 First gt gt gt class PrintName def init self value self value value printName self value def print
  • WPF 网格中的独立宽度

    我在 WPF 中有一个 2 行 2 列的网格 我希望每行的列宽都是独立的 我尝试过 自动 但没有成功 这里用一张图片来解释 我怎样才能使用网格来完成这个任务 如果您必须使用网格布局 那么您有几个选择 选项 1 将每一行设为单列 然后在您想要
  • 如何在UIView上绘制签名

    我是 ios 新手 我需要创建一个可以签名的文本视图或标签 就像这张图片 您可以在上面签名UIView首先subclass UIView和你的子类UIView应该是这样的 签名视图 h import
  • 有人能准确解释一下以下定义在 C 标准中关于指令的含义吗

    我真正需要知道的是指令开始之前允许使用哪些字符 因为我们都知道我们可以拥有new line字符和空白指令开始之前的字符前 现在我阅读了关于此的 C 标准 并找到了以下定义来解释这一点 预处理指令由一系列满足以下约束的预处理标记组成 序列中的
  • 在刀片视图中计算用户的帖子总数

    我已将博客中所有帖子的集合发送到索引视图 然后使用以下代码来计算每个用户发布的帖子总数 p class joined text Posts count App Posts where user id post gt user gt id g
  • 缺少“.NETFramework,Version=v4.0,Profile=Profile47”的定位包

    我在尝试编译时遇到了一些问题来自 f 源代码的 FSharp Core 的可移植版本 为了解决这个问题 我创建了一些项目文件 以便能够在 Visual Studio 中打开它 而不必使用以下命令 msbuild fsharp library
  • 在 C# 中组合 BinaryExpression 和 Expression>

    我怎样才能结合BinaryExpression and Expression