EFCore扩展Update方法(实现 Update User SET Id = Id + 1)

前言

  1. EFCore在操作更新的时候往往需要先查询一遍数据,再去更新相应的字段,如果针对批量更新的话会很麻烦,效率也很低。
  2. 目前github上 EFCore.Extentions 项目,实现批量更新挺方便的,但是针对 Update User SET Id = Id + 1 这种操作还是没有解决
  3. 本文主要就是扩展自更新Update

实现原理

  1. 先根据IQuaryable 获取到SQL语句
        private static readonly TypeInfo QueryCompilerTypeInfo = typeof(QueryCompiler).GetTypeInfo();
        private static readonly FieldInfo QueryCompilerField = typeof(EntityQueryProvider).GetTypeInfo().DeclaredFields.First(x => x.Name == "_queryCompiler");
        private static readonly FieldInfo QueryModelGeneratorField = QueryCompilerTypeInfo.DeclaredFields.First(x => x.Name == "_queryModelGenerator");
        private static readonly FieldInfo DataBaseField = QueryCompilerTypeInfo.DeclaredFields.Single(x => x.Name == "_database");
        private static readonly PropertyInfo DatabaseDependenciesField = typeof(Database).GetTypeInfo().DeclaredProperties.Single(x => x.Name == "Dependencies");

/// <summary>
        /// 将query 转化为sql语句
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="query"></param>
        /// <returns></returns>
        internal static string ToSql<TEntity>(this IQueryable<TEntity> query) where TEntity : class
        {
            var queryCompiler = (QueryCompiler)QueryCompilerField.GetValue(query.Provider);
            var modelGenerator = (QueryModelGenerator)QueryModelGeneratorField.GetValue(queryCompiler);
            var queryModel = modelGenerator.ParseQuery(query.Expression);
            var database = (IDatabase)DataBaseField.GetValue(queryCompiler);
            var databaseDependencies = (DatabaseDependencies)DatabaseDependenciesField.GetValue(database);
            var queryCompilationContext = databaseDependencies.QueryCompilationContextFactory.Create(false);
            var modelVisitor = (RelationalQueryModelVisitor)queryCompilationContext.CreateQueryModelVisitor();

modelVisitor.CreateQueryExecutor<TEntity>(queryModel);
            string sql = modelVisitor.Queries.First().ToString();
            return sql;
        }

  1. 把获取的查询语句的,From之前的语句砍掉,然后拼接
        public static (string, string) GetBatchSql<T>(IQueryable<T> query) where T : class, new()
        {
            string sqlQuery = query.ToSql();
            string tableAlias = sqlQuery.Substring(8, sqlQuery.IndexOf("]") - 8);
            int indexFROM = sqlQuery.IndexOf(Environment.NewLine);
            string sql = sqlQuery.Substring(indexFROM, sqlQuery.Length - indexFROM);
            sql = sql.Contains("{") ? sql.Replace("{", "{{") : sql; // Curly brackets have to escaped:
            sql = sql.Contains("}") ? sql.Replace("}", "}}") : sql; // https://github.com/aspnet/EntityFrameworkCore/issues/8820
            return (sql, tableAlias);
        }
  1. 根据传入的表达式Expression<Func<T,bool> 生成(Update [a] SET) 之后要更新的部分.列如:Expression<Func<T,bool> expression=a=>a.Id == a.Id + 1 生成[a].[Id]=[a].[Id]+parm_0 pram_0=1

通过分析Expression的节点[NodeType]来生成相应的操作符,递归拼接sql语句和参数

        public static void CreateUpdateBody(string Param, Expression expression, ref StringBuilder sb, ref List<SqlParameter> sp)
        {
            if (expression is BinaryExpression binaryExpression)
            {
                CreateUpdateBody(Param, binaryExpression.Left, ref sb, ref sp);

switch (binaryExpression.NodeType)
                {
                    case ExpressionType.Add:
                        sb.Append(" +");
                        break;
                    case ExpressionType.Divide:
                        sb.Append(" /");
                        break;
                    case ExpressionType.Multiply:
                        sb.Append(" *");
                        break;
                    case ExpressionType.Subtract:
                        sb.Append(" -");
                        break;
                    case ExpressionType.And:
                        sb.Append(" ,");
                        break;
                    case ExpressionType.AndAlso:
                        sb.Append(" ,");
                        break;
                    case ExpressionType.Or:
                        sb.Append(" ,");
                        break;
                    case ExpressionType.OrElse:
                        sb.Append(" ,");
                        break;
                    case ExpressionType.Equal:
                        sb.Append(" =");
                        break;
                    default: break;
                }

CreateUpdateBody(Param, binaryExpression.Right, ref sb, ref sp);
            }

if (expression is ConstantExpression constantExpression)
            {
                var parmName = $"param_{sp.Count}";
                sp.Add(new SqlParameter(parmName, constantExpression.Value));
                sb.Append($" @{parmName}");
            }

if (expression is MemberExpression memberExpression)
            {
                sb.Append($"{Param}.[{memberExpression.Member.Name}]");
            }
        }

  1. 最后执行生成SQL语句,详情请看源码github

  2. 调用

        static void Main(string[] args)
        {
            using (var context = new TestContext())
            {
                var list = context.User.Select<UserModel>().ToList();

var user1 = context.User.AsNoTracking().FirstOrDefault(x => x.Id == 2);

Console.WriteLine($"-----------Before Update --------------------");
                Console.WriteLine($"{user1.Id}:{user1.Name}:{user1.RoleId}");

context.User.Where(x => x.Id == 2).RestValue(x => x.Name == (x.Name + " Add Bob") && x.RoleId == (x.RoleId + 1));

var user2 = context.User.AsNoTracking().FirstOrDefault(x => x.Id == 2);

Console.WriteLine($"-----------After Update --------------------");
                Console.WriteLine($"{user2.Id}:{user2.Name}:{user2.RoleId}");
            }
            Console.WriteLine($"------------结束--------------------");
            Console.ReadLine();
        }

  1. 生成的SQL如下
     UPDATE [x] SET x.[Name] =x.[Name] + @param_0 ,x.[RoleId] =x.[RoleId] + @param_1
FROM [User] AS [x]
WHERE [x].[Id] = 2

EFCore扩展Update方法(实现 Update User SET Id=Id+1)的更多相关文章

  1. EFCore扩展Select方法(根据实体定制查询语句)

    EFCore扩展Select方法(根据实体定制查询语句)  通常用操作数据库的时候查询返回的字段是跟 我们的定义的实体是不一致的,所以往往针对UI或者接口层创建大量的Model, 而且需要手动对应字段 ...

  2. MongoDB学习笔记~Update方法更新集合属性后的怪问题

    回到目录 在对MongoDB进行封装后,对于Update更新对象里的集合属性时出现了一个现象,让人感到很恶心,人家更新前是个美丽的Array,但是更新之后集合对象变成了键值对,键是集合的类型名称,值是 ...

  3. hibernate persist update 方法没有正常工作(不保存数据,不更新数据)

    工程结构 问题描述 在工程中通过spring aop的方式配置事务,使用hibernate做持久化.在代码实现中使用hibernate persit()方法插入数据到数据库,使用hibernate u ...

  4. MongoDB中insert方法、update方法、save方法简单对比

    MongoDB中insert方法.update方法.save方法简单对比 1.update方法 该方法用于更新数据,是对文档中的数据进行更新,改变则更新,没改变则不变. 2.insert方法 该方法用 ...

  5. hibernate中保存一个对象后再设置此对象的属性为什么不需要调用update方法了

    hibernate中保存一个对象后再设置此对象的属性为什么不需要调用update方法了 例如session.save(user);user.setAge(20); 原因: hibernate对象的三种 ...

  6. Unity3d中Update()方法的替身

    在网上看到一些资料说Unity3d的Update方法是如何如何不好,影响性能.作为一个菜鸟,之前我还觉得挺好用的,完全没用什么影响性能的问题存在.现在发现确实有很大的问题,我习惯把一大堆检测判断放在U ...

  7. [原创]java WEB学习笔记79:Hibernate学习之路--- 四种对象的状态,session核心方法:save()方法,persist()方法,get() 和 load() 方法,update()方法,saveOrUpdate() 方法,merge() 方法,delete() 方法,evict(),hibernate 调用存储过程,hibernate 与 触发器协同工作

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  8. Hibernate中的session对象update方法的使用

    使一个游离对象转变为持久化对象.例如以下代码在session1中保存了一个Customer对象,然后在session2中更新这个Customer对象: Customer customer = new ...

  9. 05 - 替换vtkDataObject中的Update方法 VTK 6.0 迁移

    VTK6 引入了许多不兼容的变.其中之一是删除vtkDataObject中所有有关管道的方法.下面讨论update方法并提供迁移现有代码的建议. Update() vtkDataObject::Upd ...

随机推荐

  1. djano-cbv模式

    cbv,class base view 就是用类写视图 详细用法替换fbv将在后续空余时间上补齐 fbv,function base view 就是用函数写视图 创建django app01项目 ur ...

  2. 环境搭建:Vue环境搭建和项目初始化(ubuntu)

    1.    安装node.js(版本6.10.3) 首先确保系统安装来gcc,g++,如果没有则安装: $ sudo apt-get update $ sudo apt-get install gcc ...

  3. 虚拟机在 OpenStack 里没有共享存储条件下的在线迁移

    虚拟机在 OpenStack 里没有共享存储条件下的在线迁移 本文尝试回答与 Live migration 相关的几个问题:Live migration 是什么?为什么要做 Live migratio ...

  4. 04.CSS的继承性和层叠性

    CSS有两大特性:  继承性和层叠性 继承性 面向对象语言都会存在继承的概念 , 在面向对象语言中, 继承的特点:  继承了父类的属性和方法.  那么 css  就是在设置属性的 ,  不会牵扯到方法 ...

  5. Java微信公众平台开发(十三)--微信JSSDK中Config配置

    转自:http://www.cuiyongzhi.com/post/57.html 前端开发工程师和关注前端开发的开发者们在2015年中肯定被腾讯的JSSDk引爆过,搞APP的.搞前端的甚至是是搞后端 ...

  6. MySQL分组条件,group by order by limit 顺序

    having 中如果没有用聚合函数(必须sum,min),涉及到的字段名称必须在select 中有对应字段名称才可以,用到聚合函数可以不必在select中有相应字段名称的 limit 2,3:2表示从 ...

  7. pom.xml配置指定仓库

    <repositories> <repository> <id>central</id><--中央仓库--> <url>http ...

  8. Perl 数据类型:标量、数组、哈希

    Perl 数据类型Perl 是一种弱类型语言,所以变量不需要指定类型,Perl 解释器会根据上下文自动选择匹配类型. Perl 有三个基本的数据类型:标量.数组.哈希.以下是这三种数据类型的说明: 序 ...

  9. vector的简单用法

    vector是C++中容器的一种,与普通的数组相比,它可以动态的增长,而且还有封装了用于顺序表的操作的方法. 使用vector定义了容器之后,如果定义了容器的大小,则可以在大小范围之内直接使用数组的方 ...

  10. c++ 门面模式(Facade)

    门面模式是比较常用的一种设计模式,我们可能在无意中就会使用,门面模式就是用一个门面类来处理子系统的复杂关系,门面类简单的Api接口供客户端调用.用一个简单的演播室来表示. #include <i ...