为实体框架创建动态表达式

2023-11-22

我创建了一个通用表达式生成器,它根据条件集合构建谓词。我将谓词传递给存储库中的通用方法。我认为表达式生成器工作正常并创建所需的谓词,尽管实体框架生成的 SQL 脚本不符合我的预期。我读过很多关于动态查询或 LinqKit 和表达式生成器的问题和文章,最相关的是这条评论。如果您能检查我所做的事情并让我知道我是否犯了任何错误,我真的很感激?

这是 ExpressionBuilder 类的代码:

public static class ExpressionBuilder
{
    private static MethodInfo containsMethod = typeof(string).GetMethod("Contains");
    private static MethodInfo startsWithMethod = typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) });
    private static MethodInfo endsWithMethod = typeof(string).GetMethod("EndsWith", new Type[] { typeof(string) });

    public static Expression<Func<T, bool>> GetExpression<T>(IList<ExpressionModel> filters)
    {
        if (filters == null)
            return null;

        IList<ExpressionModel> nullFreeCollection = filters.OfType<ExpressionModel>().ToList();

        if (nullFreeCollection.Count == 0)
            return null;

        ParameterExpression param = Expression.Parameter(typeof(T), "item");
        Expression exp = null;

        if (nullFreeCollection.Count == 1)
            exp = GetExpression<T>(param, nullFreeCollection[0]);
        else if (nullFreeCollection.Count == 2)
            exp = GetExpression<T>(param, nullFreeCollection[0], nullFreeCollection[1]);
        else
        {
            while (nullFreeCollection.Count > 0)
            {
                var f1 = nullFreeCollection[0];
                var f2 = nullFreeCollection[1];

                if (exp == null)
                    exp = GetExpression<T>(param, nullFreeCollection[0], nullFreeCollection[1]);
                else
                    exp = Expression.AndAlso(exp, GetExpression<T>(param, nullFreeCollection[0], nullFreeCollection[1]));

                nullFreeCollection.Remove(f1);
                nullFreeCollection.Remove(f2);

                if (nullFreeCollection.Count == 1)
                {
                    exp = Expression.AndAlso(exp, GetExpression<T>(param, nullFreeCollection[0]));
                    nullFreeCollection.RemoveAt(0);
                }
            }
        }

        return Expression.Lambda<Func<T, bool>>(exp, param);
    }

    private static Expression GetExpression<T>(ParameterExpression param, ExpressionModel filter)
    {
        MemberExpression member = Expression.Property(param, filter.PropertyName);
        ConstantExpression constant = Expression.Constant(filter.Value);

        switch (filter.Operator)
        {
            case ExpressionOperators.Equals:
                return Expression.Equal(member, constant);
            case ExpressionOperators.GreaterThan:
                return Expression.GreaterThan(member, constant);
            case ExpressionOperators.LessThan:
                return Expression.LessThan(member, constant);
            case ExpressionOperators.GreaterThanOrEqual:
                return Expression.GreaterThanOrEqual(member, constant);
            case ExpressionOperators.LessThanOrEqual:
                return Expression.LessThanOrEqual(member, constant);
            case ExpressionOperators.Contains:
                return Expression.Call(member, containsMethod, constant);
            case ExpressionOperators.StartsWith:
                return Expression.Call(member, startsWithMethod, constant);
            case ExpressionOperators.EndsWith:
                return Expression.Call(member, endsWithMethod, constant);
        }

        return null;
    }

    private static BinaryExpression GetExpression<T>(ParameterExpression param, ExpressionModel filter1, ExpressionModel filter2)
    {
        Expression bin1 = GetExpression<T>(param, filter1);
        Expression bin2 = GetExpression<T>(param, filter2);

        return Expression.AndAlso(bin1, bin2);
    }

    public enum ExpressionOperators
    {
        Equals,
        GreaterThan,
        LessThan,
        GreaterThanOrEqual,
        LessThanOrEqual,
        Contains,
        StartsWith,
        EndsWith
    }
}

这是通用存储库方法:

    public IEnumerable<TEntity> RetrieveCollectionAsync(Expression<Func<TEntity, bool>> predicate)
    {
        try
        {
            return DataContext.Set<TEntity>().Where(predicate);
        }
        catch (Exception ex)
        {
            Logger.Error(ex);
            throw ex;
        }
    }

并由 Entity Framework for Sql 生成脚本(我期望带有一些 where 子句的选择查询):

SELECT 
    CAST(NULL AS uniqueidentifier) AS [C1], 
    CAST(NULL AS uniqueidentifier) AS [C2], 
    CAST(NULL AS varchar(1)) AS [C3], 
    CAST(NULL AS uniqueidentifier) AS [C4], 
    CAST(NULL AS uniqueidentifier) AS [C5], 
    CAST(NULL AS uniqueidentifier) AS [C6], 
    CAST(NULL AS datetime2) AS [C7], 
    CAST(NULL AS datetime2) AS [C8], 
    CAST(NULL AS varchar(1)) AS [C9], 
    CAST(NULL AS uniqueidentifier) AS [C10], 
    CAST(NULL AS varchar(1)) AS [C11], 
    CAST(NULL AS uniqueidentifier) AS [C12], 
    CAST(NULL AS uniqueidentifier) AS [C13], 
    CAST(NULL AS uniqueidentifier) AS [C14], 
    CAST(NULL AS uniqueidentifier) AS [C15], 
    CAST(NULL AS datetime2) AS [C16], 
    CAST(NULL AS varchar(1)) AS [C17], 
    CAST(NULL AS datetime2) AS [C18], 
    CAST(NULL AS varchar(1)) AS [C19], 
    CAST(NULL AS tinyint) AS [C20]
    FROM  ( SELECT 1 AS X ) AS [SingleRowTable1]
    WHERE 1 = 0

我在用

  • 实体框架6.0
  • .Net框架4.6
  • ASP.NET MVC 5

Update表达模型:

public class ExpressionModel
{
    public string PropertyName { get; set; }
    public ExpressionOperators Operator { get; set; }
    public object Value { get; set; }
}

另一个缺失的部分是一个通用映射器,它将给定的搜索条件映射到一个新的 ExpressionModel,我认为它与这个问题无关。


正如我在评论中提到的,实现过于复杂。

一、这个方法

private static BinaryExpression GetExpression<T>(ParameterExpression param, ExpressionModel filter1, ExpressionModel filter2)

检查过滤器计数、删除已处理项目等的整个逻辑是多余的。AND条件可以很容易地像这样链接起来

((Condition1 AND Condition2) AND Condition3) AND Condition4 ...

所以只需删除该功能即可。

二、这个功能

private static Expression GetExpression<T>(ParameterExpression param, ExpressionModel filter)

命名不好,不需要通用的T因为里面没有使用。

相反,将签名更改为

private static Expression MakePredicate(ParameterExpression item, ExpressionModel filter)
{
    // implementation (same as posted)
}

最后,公共方法很简单:

public static Expression<Func<T, bool>> MakePredicate<T>(IEnumerable<ExpressionModel> filters)
{
    if (filters == null) return null;
    filters = filters.Where(filter => filter != null);
    if (!filters.Any()) return null;
    var item = Expression.Parameter(typeof(T), "item");
    var body = filters.Select(filter => MakePredicate(item, filter)).Aggregate(Expression.AndAlso);
    var predicate = Expression.Lambda<Func<T, bool>>(body, item);
    return predicate;
}

附:并且不要忘记做null检查使用情况:

// should not be called Async
public IEnumerable<TEntity> RetrieveCollectionAsync(Expression<Func<TEntity, bool>> predicate)
{
    try
    {
        var query = DataContext.Set<TEntity>().AsQueryable();
        if (predicate != null)
            query = query.Where(predicate);
        return query;
    }
    catch (Exception ex)
    {
        Logger.Error(ex);
        throw ex; // should be: throw;
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为实体框架创建动态表达式 的相关文章

  • 为什么 F# 的默认集合是排序的,而 C# 的不是?

    当从 C 世界迁移到 F 最惯用的可能 思维方式时 我发现了这个有趣的差异 在 C 的 OOP mutable 世界中 默认的集合集合似乎是HashSet https learn microsoft com en us dotnet api
  • 在路由mvc 4中添加公司名称

    我一直在尝试为 Facebook 等用户提供在 URL 中添加公司名称的选项 http localhost 50753 MyCompany Login 我尝试过不同的网址 但没有成功 routes MapRoute name Default
  • 在现代 C++ 中,临时生命周期延长何时有用?

    在 C 中 您可以将函数的返回值 返回值 而不是引用 绑定到 const 引用 并且代码仍然有效 因为该临时对象的生命周期将延长到作用域末尾 例如 std string get string return abc void f const
  • C# 5 async/await 线程机制感觉不对?

    为什么让调用线程进入异步方法直到内部 等待 一旦调用异步方法就生成一个线程 这不是更干净吗 这样您就可以确定异步方法会立即返回 您不必担心在异步方法的早期阶段没有做任何昂贵的事情 我倾向于知道某个方法是否要在 我的 线程上执行代码 不管是堵
  • 将表(行)与 OpenXML SDK 2.5 保持在一起

    我想在 Word 文档中生成多个表 每行 2 行 但我想将这两行保留在一起 如果可能的话 new KeepNext 第一行不起作用 new KeepNext 第一行的最后一段不起作用 new CantSplit 放在桌子上不起作用 在所有情
  • 使用查询表达式对 List 进行排序

    我在使用 Linq 订购这样的结构时遇到问题 public class Person public int ID get set public List
  • 根据对象变量搜索对象列表

    我有一个对象列表 这些对象具有三个变量 ID 名称和值 这个列表中可能有很多对象 我需要根据ID或Name找到一个对象 并更改值 例子 class objec public string Name public int UID public
  • UI 函数在快速事件完成之前触发

    我有一个停靠在 Silverlight 应用程序中的 Web 浏览器框架 有时会在其上弹出全窗口 XAML Silverlight UI 元素 我已经或多或少修复了一个老问题 即 Web 框架的内容似乎与 Silverlight 内容不能很
  • 为什么 Cdecl 调用在“标准”P/Invoke 约定中经常不匹配?

    我正在开发一个相当大的代码库 其中 C 功能是从 C P Invoked 的 我们的代码库中有很多调用 例如 C extern C int stdcall InvokedFunction int 使用相应的 C DllImport CPlu
  • 如何从 Rx Subscribe 回调异步函数?

    我想回调 Rx 订阅中的异步函数 例如 像那样 public class Consumer private readonly Service service new Service public ReplaySubject
  • 逆向工程 ASP.NET Web 应用程序

    我有一个 ASP NET Web 应用程序 我没有源代码 该 bin 包含 10 个程序集和一个 compiled 文件 我在 App Code dll 上使用 Reflector 它向我显示了类和命名空间之类的东西 但它太混乱了 有没有什
  • .NET 4 的条件编译[重复]

    这个问题在这里已经有答案了 可能的重复 条件编译和框架目标 https stackoverflow com questions 2923210 c sharp conditional compilation and framework ta
  • 在 C#.NET 中安全删除文件

    在我正在做的一个项目中 我想为用户提供 安全 删除文件的选项 例如 用随机位或 0 覆盖它 在 C NET 中是否有一种简单的方法可以做到这一点 效果如何 你可以调用系统内部删除 http technet microsoft com en
  • C++ 中 void(*)() 和 void(&)() 之间的区别[重复]

    这个问题在这里已经有答案了 在此示例代码中 func1是类型void int double and funky是类型void int double include
  • 为什么以下 C 程序会出现总线错误?

    我认为这是第一个失败的 strtok 调用 好久没写C了 有点不知所措 非常感谢 include
  • LINQ 中的“from..where”或“FirstOrDefault”

    传统上 当我尝试从数据库中获取用户的数据时 我使用了以下方法 在某种程度上 DbUsers curUser context DbUsers FirstOrDefault x gt x u LoginName id string name c
  • 如何得知客户端从服务器的下载速度?

    根据客户的下载速度 我想以低质量或高质量显示视频 任何 Javascript 或 C 解决方案都是可以接受的 Thanks 没有任何办法可以确定 您只能测量向客户端发送数据的速度 如果没有来自客户端的任何类型的输入来表明其获取信息的速度 您
  • INotifyPropertyChanged 和 propertyName

    我一直不确定它的含义propertyName实施时INotifyPropertyChanged 所以一般来说你实现INotifyPropertyChanged as public class Data INotifyPropertyChan
  • DataContractSerializer 事件/委托字段问题

    在我的 WPF 应用程序中 我正在使用DataContractSerializer序列化对象 我发现它无法序列化具有事件或委托声明的类型 考虑以下失败的代码 Serializable public abstract class BaseCl
  • 使用未分配的局部变量

    我遇到了一个错误 尽管声明了变量 failturetext 和 userName 错误仍然出现 谁能帮帮我吗 Use of Unassigned local variable FailureText Use of Unassigned lo

随机推荐

  • PostgreSQL:加速数百万行表中的 SELECT 查询

    我有一个包含 gt 450 万行的表 我的SELECT查询对于我的需求来说太慢了 该表是用以下命令创建的 CREATE TABLE all legs carrier TEXT dep hub TEXT arr hub TEXT dep dt
  • 使用 Passport-saml 注销:req.logout() 或 Strategy.logout(),或两者?

    我有一个关于使用 Passport saml 进行身份验证时注销用户的正确方法的问题 使用 Passport saml 的示例脚本显示注销如下 app get logout function req res req logout res r
  • 无法在打字稿中导入 svg 文件

    In typescript tsx 文件我无法使用以下语句导入 svg 文件 import logo from logo svg 转译器 说 ts cannot find module logo svg 我的 svg 文件只是
  • 导出签名的应用程序而不“优化”png图像

    当我从 eclipse 导出 apk 时 它会压缩所有 png 图像 例如 一张 4 6 KB 的 png 在 apk 中变成了 2 15 KB 不过我更喜欢使用优化 png 文件tinypng在构建 apk 之前 这会生成更小的文件 同样
  • Composer 没有为包生成 .git 文件夹

    这很奇怪 似乎是我错过的一些小事 前几天 当我composer install我得到了自己的供应商目录 git 这允许我进行更改并更新我自己的打包存储库 今天跑步后composer install the git每个包文件夹内的目录丢失 我
  • 烛台多个 Y 值

    我的任务是在 Windows 窗体中使用 MSChart 制作烛台图 我已经成功地制作了 3D 条形图 没有任何问题 但在互联网 微软的源代码 WinSamples 和大量的搜索之后 我找不到创建烛台图的正确方法 可以帮助我的是一个清楚的例
  • mat 不是数值元组:openCV 错误

    我写下了一段显示错误的代码 但我没有得到它 请帮助 它的显示垫不是数字元组 import cv import cv2 capture cv2 VideoCapture j 3gp while 1 frame1 capture read gr
  • java复制构造函数和继承

    经过一番搜索后 我没有找到有关复制构造函数和继承的问题的任何好的答案 我有两个班级 用户和实习生 Trainee继承自User 并为Trainee添加两个String参数 现在我成功地创建了 User 的复制构造函数 但我对 Trainee
  • 将 Visual Studio 2012 导出为 2010 sln 格式

    我的老师抱怨他无法在 VS2010 环境中读取 VS2012 格式 我在设置等中环顾四周 但找不到任何东西 如何将 VS2010 可读格式的项目提供给我的老师 手动修改sln 备份您的项目文件夹 复制 粘贴到另一个位置 例如名为 backu
  • 无法安装 npm 包:“npm ERR!errno -4048”

    我正在尝试安装 npm 包 node xmpp server 但安装失败 以前 当我尝试安装全局安装而不是本地安装的软件包时 但现在根本不安装 我以管理员身份运行 bash I ran npm install node xmpp serve
  • ISR和多线程程序中的C'Volatile'关键字?

    我读到了 C 的用法volatile内存映射硬件寄存器 ISR 和多线程程序中的关键字 1 注册 uint8 t volatile pReg while pReg 0 do sth pReg point to status register
  • 如何知道DIV中的所有元素是否已完全加载?

    有一个div其中将通过 Ajax 加载一些元素 图像 iframe 等 这些元素完全加载后 我需要执行一个函数div 如何确定数组中的所有元素div已满载 我使用 jQuery 作为库 对于图像和 iframe 您可以使用load even
  • 将MySQL的POINT转换为PHP中的文本

    使用 PHP 如何将值转换为POINT数据类型为字符串 例如POINT 34 601020 58 371020 最好以 WKT 或 GeoJSON 形式输出 如果我回显原始值 我会得到奇怪的字符 我尝试过使用bin2hex然后尝试将十六进制
  • for循环中的lambda仅取最后一个值[重复]

    这个问题在这里已经有答案了 我有代码 options INFO WARNING DEBUG for i in range len options option options i cMenu add command label format
  • Perl 查找函数

    这个问题就解决了 非常感谢 我的问题和我正在使用的解决方案如下所述 问题 open IN lt test txt seek IN 10 0 read IN temp 5 seek IN 20 0 close IN 情况是 我的句柄将从位置
  • PostgreSQL 查询时间过长

    我的数据库有几亿行 我正在运行以下查询 select from Payments as p inner join PaymentOrders as po on po Id p PaymentOrderId inner join Users
  • 在 C++ 中的输出字符串中插入“£”符号

    我有以下代码 cout lt lt String that includes a sign 但是 编译器无法识别 符号 而是显示带重音符号的 u 我可以使用以下方法插入一个 cout lt lt String that includes a
  • 如何在数据表中引用数据表父级?

    考虑一个虚拟案例
  • 如何更改 Angular2 TypeScript 项目中的 body 类

    我的登录页面和应用程序中的其他页面有不同的类 因此用户登录后我需要更改 body 元素的类 这是我如何努力实现这一目标的 索引 html
  • 为实体框架创建动态表达式

    我创建了一个通用表达式生成器 它根据条件集合构建谓词 我将谓词传递给存储库中的通用方法 我认为表达式生成器工作正常并创建所需的谓词 尽管实体框架生成的 SQL 脚本不符合我的预期 我读过很多关于动态查询或 LinqKit 和表达式生成器的问