1. 程式人生 > >使用Asp.Net Core MVC 開發項目實踐[第四篇:基於EF Core的擴展2]

使用Asp.Net Core MVC 開發項目實踐[第四篇:基於EF Core的擴展2]

拉姆達表達式 接下來 col ron private cte 使用 下載 成員

上篇我們說到了基於EFCore的基礎擴展,這篇我們講解下基於實體結合拉姆達表達式的自定義更新以及刪除數據.

先說下原理:其實通過實體以及拉姆達表達式生成SQL語句去執行

第一種更新擴展:

自定義更新字段以及自定義擴展條件,請看下面的代碼

 1         /// <summary>
 2         /// 自定義更新擴展
 3         /// </summary>
 4         /// <typeparam name="TEntity"></typeparam>
 5         /// <param name="context"></param>
6 /// <param name="fields">更新字段</param> 7 /// <param name="predicate">更新條件</param> 8 /// <returns></returns> 9 public static bool MangoUpdate<TEntity>(this DbContext context, Expression<Func<TEntity, bool>> fields, Expression<Func<TEntity, bool
>> predicate) where TEntity : class, new() 10 { 11 TSqlAssembledResult result = TSqlAssembled.Update<TEntity>(fields, predicate); 12 context.Database.ExecuteSqlCommand(result.SqlStr); 13 return context.SaveChanges() > 0 ? true : false; 14
}

從上面的方法中我們看到幾個參數,第一個參數不必說,擴展方法第一個參數必須要的,我們重點講清楚一下第二個和第三個參數.

參數:

Expression<Func<TEntity, bool>> fields 

表示實體中需要更新的字段,這裏的參數要求的是一個拉姆達表達式,如下面的代碼:

m => m.ClickCount == m.ClickCount + 1

這裏就是更新字段ClickCount+1的功能.

參數:

Expression<Func<TEntity, bool>> predicate

表示更新條件,這個參數也是一個拉姆達表達式,如下面代碼:

m => m.NavigationId == navigationId

這裏表示更新條件 NavigationId指定值的數據庫記錄.

接下來我們看方法中的調用

 TSqlAssembled.Update<TEntity>(fields, predicate);

這個方法表示將參數解析成SQL語句,我們看看這個方法的具體內容:

 1         /// <summary>
 2         /// 更新語句組裝
 3         /// </summary>
 4         /// <typeparam name="TEntity"></typeparam>
 5         /// <param name="fields"></param>
 6         /// <param name="predicate"></param>
 7         /// <returns></returns>
 8         public static TSqlAssembledResult Update<TEntity>(Expression<Func<TEntity, bool>> fields, Expression<Func<TEntity, bool>> predicate) where TEntity : class, new()
 9         {
10             try
11             {
12                 StringBuilder strBuilder = new StringBuilder();
13                 strBuilder.Append("update ");
14                 strBuilder.Append(typeof(TEntity).Name);
15                 strBuilder.Append(" set ");
16                 //解析需要更新的字段值
17                 UpdateFieldBuilder updateFieldBuilder = new UpdateFieldBuilder();
18                 strBuilder.Append(updateFieldBuilder.Translate(fields));
19                 //解析條件
20                 ConditionBuilder conditionBuilder = new ConditionBuilder();
21                 strBuilder.Append(" where ");
22                 strBuilder.Append(conditionBuilder.Translate(predicate));
23                 //處理結果返回
24                 TSqlAssembledResult result = new TSqlAssembledResult();
25                 result.SqlParameters = null;
26                 result.SqlStr = strBuilder.ToString();
27                 return result;
28             }
29             catch(Exception ex)
30             {
31                 return null;
32                 throw ex;
33             }
34         }

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);
            return context.SaveChanges() > 0 ? 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();
                int index = 0;
                List<SqlParameter> sqlParameter = new List<SqlParameter>();
                foreach (var property in properties)
                {
                    object value = property.GetValue(entity, null);
                    if (value != null)
                    {
                        if (index != 0)
                        {
                            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:這裏我們多了將實體反射獲取需要更新的字段以及字段值.

第三種刪除擴展:

自定刪除條件,代碼如下

 1         /// <summary>
 2         /// 自定義刪除擴展
 3         /// </summary>
 4         /// <typeparam name="TEntity"></typeparam>
 5         /// <param name="context"></param>
 6         /// <param name="predicate">刪除條件</param>
 7         /// <returns></returns>
 8         public static bool MangoRemove<TEntity>(this DbContext context,Expression<Func<TEntity, bool>> predicate) where TEntity : class,new()
 9         {
10             TSqlAssembledResult result = TSqlAssembled.Delete<TEntity>(predicate);
11             context.Database.ExecuteSqlCommand(result.SqlStr);
12             return context.SaveChanges() > 0 ? true : false;
13         }

參數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):

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Text;
  4 using System.Linq;
  5 using System.Linq.Expressions;
  6 using System.Reflection;
  7 namespace Mango.Framework.EFCore
  8 {
  9     public class ConditionBuilder : ExpressionVisitor
 10     {
 11 
 12         StringBuilder strBuilder;
 13 
 14         public ConditionBuilder()
 15         {
 16         }
 17 
 18         public string Translate(Expression expression)
 19         {
 20             this.strBuilder = new StringBuilder();
 21             this.Visit(expression);
 22             return this.strBuilder.ToString();
 23         }
 24 
 25         private static Expression StripQuotes(Expression e)
 26         {
 27             while (e.NodeType == ExpressionType.Quote)
 28             {
 29                 e = ((UnaryExpression)e).Operand;
 30             }
 31             return e;
 32         }
 33 
 34         protected override Expression VisitBinary(BinaryExpression b)
 35         {
 36             strBuilder.Append("(");
 37             this.Visit(b.Left);
 38             switch (b.NodeType)
 39             {
 40                 case ExpressionType.AndAlso:
 41                     strBuilder.Append(" and ");
 42                     break;
 43                 case ExpressionType.OrElse:
 44                     strBuilder.Append(" or ");
 45                     break;
 46                 case ExpressionType.Equal:
 47                     strBuilder.Append(" = ");
 48                     break;
 49                 case ExpressionType.NotEqual:
 50                     strBuilder.Append(" <> ");
 51                     break;
 52                 case ExpressionType.LessThan:
 53                     strBuilder.Append(" < ");
 54                     break;
 55                 case ExpressionType.LessThanOrEqual:
 56                     strBuilder.Append(" <= ");
 57                     break;
 58                 case ExpressionType.GreaterThan:
 59                     strBuilder.Append(" > ");
 60                     break;
 61                 case ExpressionType.GreaterThanOrEqual:
 62                     strBuilder.Append(" >= ");
 63                     break;
 64                 default:
 65                     throw new NotSupportedException(string.Format("運算符{0}不支持", b.NodeType));
 66             }
 67             if (b.Right.NodeType != ExpressionType.Parameter&& b.Right.NodeType == ExpressionType.MemberAccess)
 68             {
 69                 LambdaExpression lambda = Expression.Lambda(b.Right);
 70                 var fn = lambda.Compile();
 71                 this.Visit(Expression.Constant(fn.DynamicInvoke(null), b.Right.Type));
 72             }
 73             else
 74             { 
 75                 this.Visit(b.Right);
 76             }
 77             strBuilder.Append(")");
 78             return b;
 79         }
 80 
 81         protected override Expression VisitConstant(ConstantExpression c)
 82         {
 83             switch (Type.GetTypeCode(c.Value.GetType()))
 84             {
 85                 case TypeCode.Boolean:
 86                     strBuilder.Append(((bool)c.Value) ? 1 : 0);
 87                     break;
 88                 case TypeCode.String:
 89                     strBuilder.Append("");
 90                     strBuilder.Append(c.Value);
 91                     strBuilder.Append("");
 92                     break;
 93                 case TypeCode.Object:
 94                     throw new NotSupportedException(string.Format("常量{0}不支持", c.Value));
 95                 default:
 96                     strBuilder.Append(c.Value);
 97                     break;
 98             }
 99             return c;
100         }
101 
102         protected override Expression VisitMember(MemberExpression m)
103         {
104             if (m.Expression != null && m.Expression.NodeType == ExpressionType.Parameter)
105             {
106                 strBuilder.Append(m.Member.Name);
107                 return m;
108             }
109             else if (m.Expression != null && m.Expression.NodeType == ExpressionType.Constant)
110             {
111                 LambdaExpression lambda = Expression.Lambda(m);
112                 var fn = lambda.Compile();
113                 this.Visit(Expression.Constant(fn.DynamicInvoke(null), m.Type));
114                 return m;
115             }
116             throw new NotSupportedException(string.Format("成員{0}不支持", m.Member.Name));
117         }
118     }
119 }

更新字段解析類(UpdateFieldBuilder):

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Text;
 4 using System.Linq;
 5 using System.Linq.Expressions;
 6 using System.Reflection;
 7 namespace Mango.Framework.EFCore
 8 {
 9     public class UpdateFieldBuilder : ExpressionVisitor
10     {
11         StringBuilder strBuilder;
12         public string Translate(Expression expression)
13         {
14             this.strBuilder = new StringBuilder();
15             this.Visit(expression);
16             return this.strBuilder.ToString();
17         }
18 
19         private static Expression StripQuotes(Expression e)
20         {
21             while (e.NodeType == ExpressionType.Quote)
22             {
23                 e = ((UnaryExpression)e).Operand;
24             }
25             return e;
26         }
27         protected override Expression VisitBinary(BinaryExpression b)
28         {
29             //strBuilder.Append("(");
30             this.Visit(b.Left);
31             switch (b.NodeType)
32             {
33                 case ExpressionType.Equal:
34                     strBuilder.Append("=");
35                     break;
36                 case ExpressionType.AndAlso:
37                     strBuilder.Append(",");
38                     break;
39                 case ExpressionType.Add:
40                     strBuilder.Append("+");
41                     break;
42                 case ExpressionType.Subtract:
43                     strBuilder.Append("-");
44                     break;
45                 default:
46                     throw new NotSupportedException(string.Format("運算符{0}不支持", b.NodeType));
47             }
48             this.Visit(b.Right);
49             //strBuilder.Append(")");
50             return b;
51         }
52 
53         protected override Expression VisitConstant(ConstantExpression c)
54         {
55             switch (Type.GetTypeCode(c.Value.GetType()))
56             {
57                 case TypeCode.Boolean:
58                     strBuilder.Append(((bool)c.Value) ? 1 : 0);
59                     break;
60                 case TypeCode.String:
61                     strBuilder.Append("");
62                     strBuilder.Append(c.Value);
63                     strBuilder.Append("");
64                     break;
65                 case TypeCode.Object:
66                     throw new NotSupportedException(string.Format("常量{0}不支持", c.Value));
67                 default:
68                     strBuilder.Append(c.Value);
69                     break;
70             }
71             return c;
72         }
73 
74         protected override Expression VisitMember(MemberExpression m)
75         {
76             if (m.Expression != null && m.Expression.NodeType == ExpressionType.Parameter)
77             {
78                 strBuilder.Append(m.Member.Name);
79                 return m;
80             }
81             throw new NotSupportedException(string.Format("成員{0}不支持", m.Member.Name));
82         }
83     }
84 }

到此本篇章完成,更詳細的代碼請下載源代碼查看.

使用Asp.Net Core MVC 開發項目實踐[第四篇:基於EF Core的擴展2]