never下sqlcient

【一】參數的輸入

如執行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 }
View Code

經過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 }
View Code

能夠知道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 }
View Code

看到這一行代碼,是一個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 }
View Code

能夠看到,裏面是使用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 }
View Code

這個UserNameTypeHandler對象實現了2個接口,一個是從數據庫到程序的類型轉換IReadingFromDataRecordToValueTypeHandler<char[]>,一個是將程序的char[]類型換成數據庫的string類型ICastingValueToParameterTypeHandler<string>,而User對象的UserName屬性剛好也是char[]類型。

總結:組件使用emit實現核心方法,性能不底;而且有TypeHandler的支持,應該來講大部分數據結構應該仍是能夠應付的。本人沒有測試過圖片字段的,因此在對binary等字段處理,我想能夠經過TypeHandler來實現

文章導航:

  1. never框架
  2. easySql使用xml管理帶事務的orm
  3. ioc工具easyioc
相關文章
相關標籤/搜索