never下sqlcient

【一】参数的输入

如执行update,我们写的代码应该是

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)的呢?我们跟踪一下

  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字段)。

@Id根据数组变成(@Id1,@Id2,@Id3) 参数,而@Name会不会变成(@Name1,@Name2,@Name3)?我们的参数@Id传入的是数组,而@Name是字符串(可以认为是char的数组),如果没有对string特殊处理,@Name就是(@Name1,@Name2,@Name3)这三个参数。

 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是不同的写法

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
posted @ 2019-06-26 14:10  shelldudu  阅读(268)  评论(0编辑  收藏  举报