2012年10月22日星期一

EF架构~一个规范,两个实现(续)~性能可以接受的批量增删改操作

EF架构~一个规范,两个实现(续)~性能可以接受的批量增删改操作

上回主要把数据操作规范及EF两种基类下的实现说了一下,今天主要针对ObjectContext情况下的批量操作作一个详细的说明,首先,要说明一点,批量操作不用ObjectContext提供的方法,而是使用拼SQL串的方式,将列表拼成一个SQL串,一次的发给数据库,这样在性能上绝对是一个质的飞越,本人尝试过,无论是EF还是linq to sql,它们提供的方法,对于列表的操作会产生N条SQL串(N=列表的长度)。

一个操作类型枚举:

1         /// <summary>2         /// 執行SQL的類型3         /// </summary>4         public enum SQLType5         {6             Insert,7             Update,8             Delete,9         }

调用方法很容易:

public void Insert<TEntity>(List<TEntity> list) where TEntity : class        {            DoListSQL(list, SQLType.Insert);        }public void Update<TEntity>(List<TEntity> list) where TEntity : class        {            DoListSQL<TEntity>(list, SQLType.Update);        }  public void Delete<TEntity>(List<TEntity> list) where TEntity : class        {            DoListSQL<TEntity>(list, SQLType.Delete);        }

下面是实现这样方法的代码,即SQL串在ObjectContext环境下是如何拼接的:

  1  /// <summary>  2         /// 构建Update语句串  3         /// </summary>  4         /// <typeparam name="TEntity"></typeparam>  5         /// <param name="entity"></param>  6         /// <returns></returns>  7         private Tuple<string, object[]> CreateUpdateSQL<TEntity>(TEntity entity) where TEntity : class  8         {  9             if (entity == null) 10                 throw new ArgumentException("The database entity can not be null."); 11  12             Type entityType = entity.GetType(); 13             var table = entityType.GetProperties().Where(i => i.PropertyType != typeof(EntityKey) && i.PropertyType != typeof(EntityState)).ToArray(); 14             var primaryKeyColumns = (entity as EntityObject).GetPK(); 15             if (primaryKeyColumns == null || primaryKeyColumns.Count == 0) 16                 throw new ArgumentException("The Table entity have not a primary key."); 17             List<object> arguments = new List<object>(); 18             StringBuilder builder = new StringBuilder(); 19  20             foreach (var change in table) 21             { 22                 if (primaryKeyColumns.Contains(change)) 23                     continue; 24  25                 if (arguments.Count != 0) 26                     builder.Append(", "); 27  28                 if (change.GetValue(entity, null) != null) 29                 { 30                     builder.Append(change.Name + " = {" + arguments.Count + "}"); 31  32                     if (change.PropertyType == typeof(string) || change.PropertyType == typeof(DateTime)) 33                         arguments.Add("'" + change.GetValue(entity, null).ToString().Replace("'", "char(39)") + "'"); 34                     else 35                         arguments.Add(change.GetValue(entity, null)); 36                 } 37                 else 38                 { 39                     builder.Append(change.Name + " = NULL, "); 40                 } 41             } 42  43             if (builder.Length == 0) 44                 throw new Exception("没有任何属性进行更新"); 45  46             builder.Insert(0, " UPDATE " + string.Format("[{0}]", entityType.Name) + " SET "); 47  48             builder.Append(" WHERE "); 49             bool firstPrimaryKey = true; 50  51             foreach (var primaryField in primaryKeyColumns) 52             { 53                 if (firstPrimaryKey) 54                     firstPrimaryKey = false; 55                 else 56                     builder.Append(" AND "); 57  58                 object val = entityType.GetProperty(primaryField.Name).GetValue(entity, null); 59                 builder.Append(GetEqualStatment(primaryField.Name, arguments.Count)); 60                 arguments.Add(val); 61             } 62             return new Tuple<string, object[]>(builder.ToString(), arguments.ToArray()); 63  64         } 65  66         /// <summary> 67         /// 构建Insert语句串 68         /// 主键为自增时,如果主键值为0,我们将主键插入到SQL串中 69         /// </summary> 70         /// <typeparam name="TEntity"></typeparam> 71         /// <param name="entity"></param> 72         /// <returns></returns> 73         private Tuple<string, object[]> CreateInsertSQL<TEntity>(TEntity entity) where TEntity : class 74         { 75             if (entity == null) 76                 throw new ArgumentException("The database entity can not be null."); 77  78             Type entityType = entity.GetType(); 79             var table = entityType.GetProperties().Where(i => i.PropertyType != typeof(EntityKey) && i.PropertyType != typeof(EntityState)).ToArray(); 80             var primaryKeyColumns = (entity as EntityObject).GetPK(); 81  82             List<object> arguments = new List<object>(); 83             StringBuilder fieldbuilder = new StringBuilder(); 84             StringBuilder valuebuilder = new StringBuilder(); 85  86             fieldbuilder.Append(" INSERT INTO " + string.Format("[{0}]", entityType.Name) + " ("); 87  88             foreach (var member in table) 89             { 90                 if (primaryKeyColumns.Contains(member) && Convert.ToString(member.GetValue(entity, null)) == "0") 91                     continue; 92                 object value = member.GetValue(entity, null); 93                 if (value != null) 94                 { 95                     if (arguments.Count != 0) 96                     { 97                         fieldbuilder.Append(", "); 98                         valuebuilder.Append(", "); 99                     }100 101                     fieldbuilder.Append(member.Name);102                     if (member.PropertyType == typeof(string) || member.PropertyType == typeof(DateTime))103                         valuebuilder.Append("'{" + arguments.Count + "}'");104                     else105                         valuebuilder.Append("{" + arguments.Count + "}");106                     if (value.GetType() == typeof(string))107                         value = value.ToString().Replace("'", "char(39)");108                     arguments.Add(value);109 110                 }111             }112 113 114             fieldbuilder.Append(") Values (");115 116             fieldbuilder.Append(valuebuilder.ToString());117             fieldbuilder.Append(");");118             return new Tuple<string, object[]>(fieldbuilder.ToString(), arguments.ToArray());119         }120         /// <summary>121         /// 构建Delete语句串122         /// </summary>123         /// <typeparam name="TEntity"></typeparam>124         /// <param name="entity"></param>125         /// <returns></returns>126         private Tuple<string, object[]> CreateDeleteSQL<TEntity>(TEntity entity) where TEntity : class127         {128             if (entity == null)129                 throw new ArgumentException("The database entity can not be null.");130 131             Type entityType = entity.GetType();132             var table = entityType.GetProperties().Where(i => i.PropertyType != typeof(EntityKey) && i.PropertyType != typeof(EntityState)).ToArray();133             var primaryKeyColumns = (entity as EntityObject).GetPK();134             if (primaryKeyColumns == null || primaryKeyColumns.Count == 0)135                 throw new ArgumentException("The Table entity have not a primary key.");136 137             List<object> arguments = new List<object>();138             StringBuilder builder = new StringBuilder();139             builder.Append(" Delete from " + string.Format("[{0}]", entityType.Name));140 141             builder.Append(" WHERE ");142             bool firstPrimaryKey = true;143 144             foreach (var primaryField in primaryKeyColumns)145             {146                 if (firstPrimaryKey)147                     firstPrimaryKey = false;148                 else149                     builder.Append(" AND ");150 151                 object val = entityType.GetProperty(primaryField.Name).GetValue(entity, null);152                 builder.Append(GetEqualStatment(primaryField.Name, arguments.Count));153                 arguments.Add(val);154             }155             return new Tuple<string, object[]>(builder.ToString(), arguments.ToArray());156         }157 158 159         /// <summary>160         /// 执行实体列表以SQL串的方式161         /// </summary>162         /// <typeparam name="TEntity"></typeparam>163         /// <param name="list"></param>164         private void DoListSQL<TEntity>(IEnumerable<TEntity> list, SQLType sqlType) where TEntity : class165         {166             StringBuilder sqlstr = new StringBuilder();167 168             switch (sqlType)169             {170                 case SQLType.Insert:171                     list.ToList().ForEach(i =>172                    {173                        Tuple<string, object[]> sql = CreateInsertSQL(i);174                        sqlstr.AppendFormat(sql.Item1, sql.Item2);175                    });176                     break;177                 case SQLType.Update:178                     list.ToList().ForEach(i =>179                     {180                         Tuple<string, object[]> sql = CreateUpdateSQL(i);181                         sqlstr.AppendFormat(sql.Item1, sql.Item2);182                     });183                     break;184                 case SQLType.Delete:185                     list.ToList().ForEach(i =>186                    {187                        Tuple<string, object[]> sql = CreateDeleteSQL(i);188                        sqlstr.AppendFormat(sql.Item1, sql.Item2);189                    });190                     break;191                 default:192                     throw new ArgumentException("请输入正确的参数");193             }194 195             _db.ExecuteStoreCommand(sqlstr.ToString());196         }

代码在vs2010+sql2005+mvc3环境下测试通过


TAG: