如執行update,咱們寫的代碼應該是html
sqlclient.Update("update xx set a.Moblie = '13800138000' where Id in (@Id) and Name = @Name;",new { @Id = new [] { 1,2,43 },@Name = "eee" });
表示更新Id =1,2,3這三行的信息。這裏的參數是一個匿名類型:Id是數組類型,Name是string類型。@Id參數是如何變成(@Id1,@Id2,@Id3)的呢?咱們跟蹤一下mysql
1 /// <summary> 2 /// sql語句拼接 3 /// </summary> 4 public class SqlParamerBuilder 5 { 6 /// <summary> 7 /// 準備參數 8 /// </summary> 9 /// <param name="prefix"></param> 10 /// <param name="parameter"></param> 11 /// <returns></returns> 12 public SqlParamerBuilder Build(string prefix, object @parameter) 13 { 14 if (string.IsNullOrEmpty(this.sql)) 15 return this; 16 17 if (this.builded) 18 return this; 19 20 var table = PreParameters(@parameter); 21 if (table == null || table.Count == 0) 22 { 23 this.parameters = new List<KeyValuePair<string, object>>(); 24 return this; 25 } 26 27 this.parameters = new List<KeyValuePair<string, object>>(table.Count); 28 var keys = new List<string>(table.Count); 29 sql = rxParamers.Replace(sql, m => 30 { 31 var name = m.Groups["name"].Value; 32 if (!table.ContainsKey(name)) 33 { 34 var i = m.Groups["name"].Index; 35 var count = 0; 36 while (i >= 0) 37 { 38 if (this.sql[i] == '\'') 39 { 40 count = 1; 41 break; 42 } 43 i--; 44 } 45 46 if (count > 0) 47 { 48 i = m.Groups["name"].Index; 49 while (i < this.sql.Length) 50 { 51 if (this.sql[i] == '\'') 52 { 53 count = 2; 54 break; 55 } 56 i++; 57 } 58 } 59 60 if (count != 2) 61 throw new Exception(string.Format("當前在sql語句中參數爲{0}的值在所提供的參數列表中找不到", name)); 62 63 return string.Concat(this.sql[m.Groups["name"].Index - 1], name); 64 } 65 66 var stValue = table[name] as string; 67 if (stValue != null) 68 { 69 if (!keys.Contains(name)) 70 { 71 keys.Add(name); 72 parameters.Add(new KeyValuePair<string, object>(name, table[name])); 73 } 74 75 return string.Concat(prefix, name); 76 } 77 78 var value = table[name] as IEnumerable; 79 if (value != null) 80 { 81 int totalCount = 0; 82 var ator = value.GetEnumerator(); 83 var newNameList = new List<string>(50); 84 while (ator.MoveNext()) 85 { 86 totalCount++; 87 var newkey = string.Format("{0}{1}x{2}z", prefix, name, totalCount); 88 newNameList.Add(newkey); 89 parameters.Add(new KeyValuePair<string, object>(newkey, ator.Current)); 90 } 91 92 return string.Concat(string.Join(",", newNameList.ToArray())); 93 } 94 95 if (!keys.Contains(name)) 96 { 97 keys.Add(name); 98 parameters.Add(new KeyValuePair<string, object>(name, table[name])); 99 } 100 101 return string.Concat(prefix, name); 102 }); 103 104 this.builded = true; 105 keys.Clear(); 106 return this; 107 } 108 }
經過SqlParamerBuilder的Build方法能夠知道:將匿名對象參數parameter轉成Hashtable的同時將sql語句變成了"update xx where Id in (@Id1,@Id2,@Id3) and Name = @Name,而且只是遍歷傳入參數全部的Property屬性(並不遍歷Field字段)。sql
@Id根據數組變成(@Id1,@Id2,@Id3) 參數,而@Name會不會變成(@Name1,@Name2,@Name3)?咱們的參數@Id傳入的是數組,而@Name是字符串(能夠認爲是char的數組),若是沒有對string特殊處理,@Name就是(@Name1,@Name2,@Name3)這三個參數。shell
1 //注意是string,實現了IEnumerable接口 2 var stValue = table[name] as string; 3 if (stValue != null) 4 { 5 if (!keys.Contains(name)) 6 { 7 keys.Add(name); 8 parameters.Add(new KeyValuePair<string, object>(name, table[name])); 9 } 10 11 return string.Concat(prefix, name); 12 } 13 14 var value = table[name] as IEnumerable; 15 if (value != null) 16 { 17 int totalCount = 0; 18 var ator = value.GetEnumerator(); 19 var newNameList = new List<string>(50); 20 while (ator.MoveNext()) 21 { 22 totalCount++; 23 var newkey = string.Format("{0}{1}x{2}z", prefix, name, totalCount); 24 newNameList.Add(newkey); 25 parameters.Add(new KeyValuePair<string, object>(newkey, ator.Current)); 26 } 27 28 return string.Concat(string.Join(",", newNameList.ToArray())); 29 }
能夠知道string是特殊處理,因此能夠認爲趕上了IEnumerable接口的就是數組參數。數據庫
定位到@Id這一位置是用了下面的正則數組
/// <summary> /// 分析sql語句中的參數 /// </summary> Regex rxParamers = new Regex(@"(?<prefix>(?<![?@:])[?@:](?![?@:]))(?<name>\w+)", RegexOptions.Compiled);
這個正則能夠幫咱們找到【@?:】這三種開頭的前綴,譬如上面的 where Id = @Id and Name = ?Name and Mobile = :Mobile數據結構
參數能夠是匿名類,也能夠對象。框架
對update,delete執行的方法是ExecuteNonQuery,獲得是Int類型的結果。而insert是使用ExecuteScalar,因此這個結果是看sql的語句的,打個比方,咱們拿自增Id,不一樣的sql是不一樣的寫法ide
sqlserver select @@identity; mysql select last_insert_id(); sqlite select last_insert_rowid();
對於select的結果,咱們對QueryForObject<T>方法進行分析工具
1 /// <summary> 2 /// 查詢列表 3 /// </summary> 4 /// <typeparam name="T">返回對象類型</typeparam> 5 /// <param name="command">查詢命令</param> 6 /// <param name="closeConnection">關閉數據庫鏈接</param> 7 /// <returns></returns> 8 protected virtual T QueryForObject<T>(IDbCommand command, bool closeConnection) 9 { 10 var @delegate = DataRecordBuilder<T>.Func; 11 IDataReader reader = null; 12 try 13 { 14 using (reader = this.CreateReader(command)) 15 { 16 var rd = new IDataRecordDecorator(reader); 17 if (reader.Read()) 18 { 19 return @delegate(rd.Load(reader)); 20 } 21 } 22 } 23 catch 24 { 25 throw; 26 } 27 finally 28 { 29 if (this.Transaction == null && closeConnection && reader != null && !reader.IsClosed) 30 reader.Close(); 31 } 32 33 return default(T); 34 }
看到這一行代碼,是一個Func的委託
var @delegate = DataRecordBuilder<T>.Func;
咱們定位到DataRecordBuilder<T>這一個類
1 /// <summary> 2 /// 對對象進行emit操做 3 /// </summary> 4 /// <param name="emit">The emit.</param> 5 public static void BuildObject(EasyEmitBuilder<Func<IDataRecord, T>> emit) 6 { 7 var type = typeof(T); 8 var targetMembers = GetMembers(type); 9 10 /*實例*/ 11 var instanceLocal = emit.DeclareLocal(type); 12 if (type.IsValueType) 13 { 14 if (targetMembers == null || targetMembers.Count == 0) 15 { 16 emit.LoadLocalAddress(instanceLocal); 17 emit.InitializeObject(type); 18 emit.LoadLocal(instanceLocal); 19 emit.Return(); 20 return; 21 } 22 23 emit.LoadLocalAddress(instanceLocal); 24 emit.InitializeObject(type); 25 emit.LoadLocal(instanceLocal); 26 emit.StoreLocal(instanceLocal); 27 goto _Read; 28 } 29 }
能夠看到,裏面是使用emit技術實現對對象T的屬性或字段進行讀寫與賦值的(emit是後面全部技術點的基礎)。
TypeHandler,有時候阻抗失敗使用的方式,仍是在DataRecordBuilder<T>這一個類中,咱們拿屬性進行get或set的時候,會對該屬性或字段進行查詢TypeHandlerAttribute這個attribute,
var attribute = member.GetCustomAttribute<TypeHandlerAttribute>();
那麼是怎麼用呢?拿demo一個Typehander例子來看其使用
1 public class User 2 { 3 public int Id { get; set; } 4 5 public long UserId { get; set; } 6 7 [Never.SqlClient.TypeHandler(typeof(UserNameTypeHandler))] 8 public char[] UserName { get; set; } 9 } 10 11 public class UserNameTypeHandler : IReadingFromDataRecordToValueTypeHandler<char[]>, ICastingValueToParameterTypeHandler<string> 12 { 13 /// <summary> 14 /// 15 /// </summary> 16 /// <param name="value"></param> 17 /// <returns></returns> 18 public string ToParameter(object value) 19 { 20 if (value == null) 21 return string.Empty; 22 23 return new string((char[])value); 24 } 25 26 /// <summary> 27 /// 獲取結果 28 /// </summary> 29 /// <param name="dataRecord">讀取器</param> 30 /// <param name="ordinal">column的位置,若是未-1表示沒有找到這個值</param> 31 /// <param name="columnName">行名字</param> 32 /// <returns></returns> 33 public char[] ToValue(IDataRecord dataRecord, int ordinal, string columnName) 34 { 35 var value = dataRecord.GetString(ordinal); 36 return value == null ? new char[0] : value.ToCharArray(); 37 } 38 }
這個UserNameTypeHandler對象實現了2個接口,一個是從數據庫到程序的類型轉換IReadingFromDataRecordToValueTypeHandler<char[]>,一個是將程序的char[]類型換成數據庫的string類型ICastingValueToParameterTypeHandler<string>,而User對象的UserName屬性剛好也是char[]類型。
總結:組件使用emit實現核心方法,性能不底;而且有TypeHandler的支持,應該來講大部分數據結構應該仍是能夠應付的。本人沒有測試過圖片字段的,因此在對binary等字段處理,我想能夠經過TypeHandler來實現
文章導航: