使用Asp.Net Core MVC 开发项目实践[第四篇:基于EF Core的扩展2]

时间:2022-12-31 21:14:12

上篇我们说到了基于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));
         }
     }
 }

到此本篇章完成,更详细的代码请下载源代码查看.