使用Asp.Net Core MVC 开发项目实践[第四篇:基于EF Core的扩展2]
上篇我们说到了基于EFCore的基础扩展,这篇我们讲解下基于实体结合拉姆达表达式的自定义更新以及删除数据.
先说下原理:其实通过实体以及拉姆达表达式生成SQL语句去执行
第一种更新扩展:
自定义更新字段以及自定义扩展条件,请看下面的代码
/// <summary> /// 自定义更新扩展 /// </summary> /// <typeparam name="TEntity"></typeparam> /// <param name="context"></param> /// <param name="fields">更新字段</param> /// <param name="predicate">更新条件</param> /// <returns></returns> public static bool MangoUpdate<TEntity>(this DbContext context, Expression<Func<TEntity, bool>> fields, Expression<Func<TEntity, bool>> predicate) where TEntity : class, new() { TSqlAssembledResult result = TSqlAssembled.Update<TEntity>(fields, predicate); context.Database.ExecuteSqlCommand(result.SqlStr); ? true : false; }
从上面的方法中我们看到几个参数,第一个参数不必说,扩展方法第一个参数必须要的,我们重点讲清楚一下第二个和第三个参数.
参数:
Expression<Func<TEntity, bool>> fields
表示实体中需要更新的字段,这里的参数要求的是一个拉姆达表达式,如下面的代码:
m => m.ClickCount == m.ClickCount +
这里就是更新字段ClickCount+1的功能.
参数:
Expression<Func<TEntity, bool>> predicate
表示更新条件,这个参数也是一个拉姆达表达式,如下面代码:
m => m.NavigationId == navigationId
这里表示更新条件 NavigationId指定值的数据库记录.
接下来我们看方法中的调用
TSqlAssembled.Update<TEntity>(fields, predicate);
这个方法表示将参数解析成SQL语句,我们看看这个方法的具体内容:
/// <summary> /// 更新语句组装 /// </summary> /// <typeparam name="TEntity"></typeparam> /// <param name="fields"></param> /// <param name="predicate"></param> /// <returns></returns> public static TSqlAssembledResult Update<TEntity>(Expression<Func<TEntity, bool>> fields, Expression<Func<TEntity, bool>> predicate) where TEntity : class, new() { try { StringBuilder strBuilder = new StringBuilder(); strBuilder.Append("update "); strBuilder.Append(typeof(TEntity).Name); strBuilder.Append(" set "); //解析需要更新的字段值 UpdateFieldBuilder updateFieldBuilder = new UpdateFieldBuilder(); strBuilder.Append(updateFieldBuilder.Translate(fields)); //解析条件 ConditionBuilder conditionBuilder = new ConditionBuilder(); strBuilder.Append(" where "); strBuilder.Append(conditionBuilder.Translate(predicate)); //处理结果返回 TSqlAssembledResult result = new TSqlAssembledResult(); result.SqlParameters = null; result.SqlStr = strBuilder.ToString(); return result; } catch(Exception ex) { return null; throw ex; } }
PS:这个方法中用到的条件编译类以及字段编辑类我们将在文章底部贴出来.
第二种更新扩展:
/// <summary> /// 自定义更新扩展 /// </summary> /// <typeparam name="TEntity"></typeparam> /// <param name="context"></param> /// <param name="entity">更新实体</param> /// <param name="predicate">更新条件</param> /// <returns></returns> public static bool MangoUpdate<TEntity>(this DbContext context, TEntity entity, Expression<Func<TEntity, bool>> predicate) where TEntity:class,new() { TSqlAssembledResult result = TSqlAssembled.Update<TEntity>(entity, predicate); context.Database.ExecuteSqlCommand(result.SqlStr, result.SqlParameters); ? true : false; }
参数 TEntity entity表示需要更新的实体
参数 Expression<Func<TEntity, bool>> predicate 表示更新条件,示例如下:
m => m.NavigationId == navigationId
TSqlAssembled.Update<TEntity>(entity, predicate) 这个方法表示将参数解析成SQL语句,我们看看这个方法的具体内容:
/// <summary> /// 更新语句组装 /// </summary> /// <typeparam name="TEntity"></typeparam> /// <param name="entity"></param> /// <param name="predicate"></param> /// <returns></returns> public static TSqlAssembledResult Update<TEntity>(TEntity entity, Expression<Func<TEntity, bool>> predicate) where TEntity : class, new() { try { StringBuilder strBuilder = new StringBuilder(); strBuilder.Append("update "); // Type type = entity.GetType(); strBuilder.Append(type.Name); strBuilder.Append(" set "); //处理实体类属性 PropertyInfo[] properties = type.GetProperties(); ; List<SqlParameter> sqlParameter = new List<SqlParameter>(); foreach (var property in properties) { object value = property.GetValue(entity, null); if (value != null) { ) { strBuilder.Append(","); } strBuilder.Append(property.Name); strBuilder.Append("=@"); strBuilder.Append(property.Name); sqlParameter.Add(new SqlParameter(property.Name, value)); index++; } } //编译条件 ConditionBuilder conditionBuilder = new ConditionBuilder(); strBuilder.Append(" where "); strBuilder.Append(conditionBuilder.Translate(predicate)); //处理结果返回 TSqlAssembledResult result = new TSqlAssembledResult(); result.SqlParameters = sqlParameter.ToArray(); result.SqlStr = strBuilder.ToString(); return result; } catch (Exception ex) { return null; throw ex; } }
PS:这里我们多了将实体反射获取需要更新的字段以及字段值.
第三种删除扩展:
自定删除条件,代码如下
/// <summary> /// 自定义删除扩展 /// </summary> /// <typeparam name="TEntity"></typeparam> /// <param name="context"></param> /// <param name="predicate">删除条件</param> /// <returns></returns> public static bool MangoRemove<TEntity>(this DbContext context,Expression<Func<TEntity, bool>> predicate) where TEntity : class,new() { TSqlAssembledResult result = TSqlAssembled.Delete<TEntity>(predicate); context.Database.ExecuteSqlCommand(result.SqlStr); ? true : false; }
参数Expression<Func<TEntity, bool>> predicate表示为自定义条件,示例如下:
_dbContext.MangoRemove<Entity.m_PostsAnswerRecords>(m => m.AnswerId == model.AnswerId && m.UserId == model.UserId);
PS:此段代码表示根据指定条件删除m_PostsAnswerRecords表中的记录
TSqlAssembled.Delete<TEntity>(predicate)方法负责将指定条件编译成SQL语句,代码如下:
/// <summary> /// 删除语句组装 /// </summary> /// <typeparam name="TEntity"></typeparam> /// <param name="predicate"></param> /// <returns></returns> public static TSqlAssembledResult Delete<TEntity>(Expression<Func<TEntity, bool>> predicate) where TEntity:class,new() { try { string tableName = typeof(TEntity).Name; //条件编译 ConditionBuilder conditionBuilder = new ConditionBuilder(); string conditionStr = conditionBuilder.Translate(predicate); StringBuilder strBuilder = new StringBuilder(); strBuilder.Append("delete from "); strBuilder.Append(tableName); strBuilder.Append(" where "); strBuilder.Append(conditionStr); //处理结果返回 TSqlAssembledResult result = new TSqlAssembledResult(); result.SqlParameters = null; result.SqlStr = strBuilder.ToString(); return result; } catch(Exception ex) { throw ex; } }
下面我们贴出字段以及条件的拉姆达表达式解析类:
条件解析类(ConditionBuilder):
using System; using System.Collections.Generic; using System.Text; using System.Linq; using System.Linq.Expressions; using System.Reflection; namespace Mango.Framework.EFCore { public class ConditionBuilder : ExpressionVisitor { StringBuilder strBuilder; public ConditionBuilder() { } public string Translate(Expression expression) { this.strBuilder = new StringBuilder(); this.Visit(expression); return this.strBuilder.ToString(); } private static Expression StripQuotes(Expression e) { while (e.NodeType == ExpressionType.Quote) { e = ((UnaryExpression)e).Operand; } return e; } protected override Expression VisitBinary(BinaryExpression b) { strBuilder.Append("("); this.Visit(b.Left); switch (b.NodeType) { case ExpressionType.AndAlso: strBuilder.Append(" and "); break; case ExpressionType.OrElse: strBuilder.Append(" or "); break; case ExpressionType.Equal: strBuilder.Append(" = "); break; case ExpressionType.NotEqual: strBuilder.Append(" <> "); break; case ExpressionType.LessThan: strBuilder.Append(" < "); break; case ExpressionType.LessThanOrEqual: strBuilder.Append(" <= "); break; case ExpressionType.GreaterThan: strBuilder.Append(" > "); break; case ExpressionType.GreaterThanOrEqual: strBuilder.Append(" >= "); break; default: throw new NotSupportedException(string.Format("运算符{0}不支持", b.NodeType)); } if (b.Right.NodeType != ExpressionType.Parameter&& b.Right.NodeType == ExpressionType.MemberAccess) { LambdaExpression lambda = Expression.Lambda(b.Right); var fn = lambda.Compile(); this.Visit(Expression.Constant(fn.DynamicInvoke(null), b.Right.Type)); } else { this.Visit(b.Right); } strBuilder.Append(")"); return b; } protected override Expression VisitConstant(ConstantExpression c) { switch (Type.GetTypeCode(c.Value.GetType())) { case TypeCode.Boolean: strBuilder.Append((( : ); break; case TypeCode.String: strBuilder.Append("'"); strBuilder.Append(c.Value); strBuilder.Append("'"); break; case TypeCode.Object: throw new NotSupportedException(string.Format("常量{0}不支持", c.Value)); default: strBuilder.Append(c.Value); break; } return c; } protected override Expression VisitMember(MemberExpression m) { if (m.Expression != null && m.Expression.NodeType == ExpressionType.Parameter) { strBuilder.Append(m.Member.Name); return m; } else if (m.Expression != null && m.Expression.NodeType == ExpressionType.Constant) { LambdaExpression lambda = Expression.Lambda(m); var fn = lambda.Compile(); this.Visit(Expression.Constant(fn.DynamicInvoke(null), m.Type)); return m; } throw new NotSupportedException(string.Format("成员{0}不支持", m.Member.Name)); } } }
更新字段解析类(UpdateFieldBuilder):
using System; using System.Collections.Generic; using System.Text; using System.Linq; using System.Linq.Expressions; using System.Reflection; namespace Mango.Framework.EFCore { public class UpdateFieldBuilder : ExpressionVisitor { StringBuilder strBuilder; public string Translate(Expression expression) { this.strBuilder = new StringBuilder(); this.Visit(expression); return this.strBuilder.ToString(); } private static Expression StripQuotes(Expression e) { while (e.NodeType == ExpressionType.Quote) { e = ((UnaryExpression)e).Operand; } return e; } protected override Expression VisitBinary(BinaryExpression b) { //strBuilder.Append("("); this.Visit(b.Left); switch (b.NodeType) { case ExpressionType.Equal: strBuilder.Append("="); break; case ExpressionType.AndAlso: strBuilder.Append(","); break; case ExpressionType.Add: strBuilder.Append("+"); break; case ExpressionType.Subtract: strBuilder.Append("-"); break; default: throw new NotSupportedException(string.Format("运算符{0}不支持", b.NodeType)); } this.Visit(b.Right); //strBuilder.Append(")"); return b; } protected override Expression VisitConstant(ConstantExpression c) { switch (Type.GetTypeCode(c.Value.GetType())) { case TypeCode.Boolean: strBuilder.Append((( : ); break; case TypeCode.String: strBuilder.Append("'"); strBuilder.Append(c.Value); strBuilder.Append("'"); break; case TypeCode.Object: throw new NotSupportedException(string.Format("常量{0}不支持", c.Value)); default: strBuilder.Append(c.Value); break; } return c; } protected override Expression VisitMember(MemberExpression m) { if (m.Expression != null && m.Expression.NodeType == ExpressionType.Parameter) { strBuilder.Append(m.Member.Name); return m; } throw new NotSupportedException(string.Format("成员{0}不支持", m.Member.Name)); } } }
到此本篇章完成,更详细的代码请下载源代码查看.
使用Asp.Net Core MVC 开发项目实践[第四篇:基于EF Core的扩展2]的更多相关文章
- 使用Asp.Net Core MVC 开发项目实践[第三篇:基于EF Core的扩展]
上篇我们说到了EFCore的基础使用,这篇我们将讲解下基于EFCore的扩展. 我们在Mango.Framework.EFCore类库项目中创建一个类名EFExtended的扩展类,并且引入相关的命名 ...
- 使用Asp.Net Core MVC 开发项目实践[第五篇:缓存的使用]
项目中我们常常会碰到一些数据,需要高频率用到但是又不会频繁变动的这类,我们就可以使用缓存把这些数据缓存起来(比如说本项目的导航数据,帖子频道数据). 我们项目中常用到有Asp.Net Core 本身提 ...
- 使用Asp.Net Core MVC 开发项目实践[第一篇:项目结构说明]
先从下图看整体项目结构: Mango.Manager: 为后台管理项目 Mango.Web: 为前台项目 Mango.Framework.Core: 为常用的基础操作类项目 Mango.Framewo ...
- 使用Asp.Net Core MVC 开发项目实践[第二篇:EF Core]
在项目中使用EF Core还是比较容易的,在这里我们使用的版本是EF Core 2.2. 1.使用nuget获取EF Core包 这个示例项目使用的是SQLSERVER,所以还需要下载Microsof ...
- 《ASP.NET Core应用开发入门教程》与《ASP.NET Core 应用开发项目实战》正式出版
“全书之写印,实系初稿.有时公私琐务猬集,每写一句,三搁其笔:有时兴会淋漓,走笔疾书,絮絮不休:有时意趣萧索,执笔木坐,草草而止.每写一段,自助覆阅,辄摇其首,觉有大不妥者,即贴补重书,故剪刀浆糊乃不 ...
- ASP.NET Core Web开发学习笔记-1介绍篇
ASP.NET Core Web开发学习笔记-1介绍篇 给大家说声报歉,从2012年个人情感破裂的那一天,本人的51CTO,CnBlogs,Csdn,QQ,Weboo就再也没有更新过.踏实的生活(曾辞 ...
- Asp.Net Core 2.0 项目实战(10) 基于cookie登录授权认证并实现前台会员、后台管理员同时登录
1.登录的实现 登录功能实现起来有哪些常用的方式,大家首先想到的肯定是cookie或session或cookie+session,当然还有其他模式,今天主要探讨一下在Asp.net core 2.0下 ...
- Asp.Net Core 2.0 项目实战(11) 基于OnActionExecuting全局过滤器,页面操作权限过滤控制到按钮级
1.权限管理 权限管理的基本定义:百度百科. 基于<Asp.Net Core 2.0 项目实战(10) 基于cookie登录授权认证并实现前台会员.后台管理员同时登录>我们做过了登录认证, ...
- C# 嵌入dll 动软代码生成器基础使用 系统缓存全解析 .NET开发中的事务处理大比拼 C#之数据类型学习 【基于EF Core的Code First模式的DotNetCore快速开发框架】完成对DB First代码生成的支持 基于EF Core的Code First模式的DotNetCore快速开发框架 【懒人有道】在asp.net core中实现程序集注入
C# 嵌入dll 在很多时候我们在生成C#exe文件时,如果在工程里调用了dll文件时,那么如果不加以处理的话在生成的exe文件运行时需要连同这个dll一起转移,相比于一个单独干净的exe,这种形 ...
随机推荐
- 学以致用三十四-----python2.0加载图片
想用做一个静态图片为背景的页面.结果遇到了一些阻碍.其主要原因还是路径没有找对.网上也参考了不少方法,也许是因为版本不同,处理的方法也不同,因此按照网上的处理方式,也没有得到解决. 为此困惑了一天.结 ...
- spring filter lister servlet
https://blog.csdn.net/nacey5201/article/details/8547772 https://blog.csdn.net/xwl617756974/article/d ...
- 图像处理及opencv汇总
OPENCV——C++ 1.windows基于vs2017的opencv安装 2.为opencv添加contrib库 3.opencv源码编写规则 4.OpenCV库框架结构 5.OpenCV从2到3 ...
- [翻译] Visual Studio 2019 RC版发布
[翻译] Visual Studio 2019 RC版发布 原文: Visual Studio 2019 Release Candidate (RC) now available 今天,我们将分享 V ...
- 前端开发JS白板编程题目若干
在前端开发参加面试的时候,无论是校招还是社招,往往都会碰到让我们直接在白纸或者白板上手撸代码的题目.由于是手撸代码,这些题目肯定不会过于复杂和冗长,否则面试那么一小会时间根本写不完.本文总结了几个我本 ...
- FFmpeg 学习(七):FFmpeg 学习整理总结
一.FFmpeg 播放视频的基本流程整理 播放流程: video.avi(Container) -> 打开得到 Video_Stream -> 读取Packet -> 解析到 Fra ...
- java常见面试题及部分答案
1.Redis常见的存储数据类型 list(列表类型) set(集合类型) zset(有序集合类型) string(字符串类型) hash(散装类型) 2.log4j的级别 debug:日志的最低级别 ...
- 配置MapReduce时遇到的问题记录
1.左边栏的Project Explorer里一直不出现DFS Locations. 发现在把hadoop-eclipse-plugin-2.6.0.jar放到eclipse下的pluins文件夹下并 ...
- Linux 下 pushd,popd,cd- 用法
一,为何要使用这几个命令? 可能大家会有疑问,为何要使用这几个命令, 难道用cd不就可以切换目录了吗? 没错,使用cd就可以切换到需要访问的目录, 但是有时会是一个路径很长,层次很多的目录 ...
- 机器学习入门09 - 特征组合 (Feature Crosses)
原文链接:https://developers.google.com/machine-learning/crash-course/feature-crosses/ 特征组合是指两个或多个特征相乘形成的 ...