【一】参数的输入

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

sqlclient.Update("update xx set a.Moblie = '13800138000' where Id in (@Id) and Name = @Name;",new { @Id = new [] { ,, },@Name = "eee" });

表示更新Id =1,2,3这三行的信息。这里的参数是一个匿名类型:Id是数组类型,Name是string类型。@Id参数是如何变成(@Id1,@Id2,@Id3)的呢?我们跟踪一下

 /// <summary>
/// sql语句拼接
/// </summary>
public class SqlParamerBuilder
{
/// <summary>
/// 准备参数
/// </summary>
/// <param name="prefix"></param>
/// <param name="parameter"></param>
/// <returns></returns>
public SqlParamerBuilder Build(string prefix, object @parameter)
{
if (string.IsNullOrEmpty(this.sql))
return this; if (this.builded)
return this; var table = PreParameters(@parameter);
if (table == null || table.Count == )
{
this.parameters = new List<KeyValuePair<string, object>>();
return this;
} this.parameters = new List<KeyValuePair<string, object>>(table.Count);
var keys = new List<string>(table.Count);
sql = rxParamers.Replace(sql, m =>
{
var name = m.Groups["name"].Value;
if (!table.ContainsKey(name))
{
var i = m.Groups["name"].Index;
var count = ;
while (i >= )
{
if (this.sql[i] == '\'')
{
count = ;
break;
}
i--;
} if (count > )
{
i = m.Groups["name"].Index;
while (i < this.sql.Length)
{
if (this.sql[i] == '\'')
{
count = ;
break;
}
i++;
}
} if (count != )
throw new Exception(string.Format("当前在sql语句中参数为{0}的值在所提供的参数列表中找不到", name)); return string.Concat(this.sql[m.Groups["name"].Index - ], name);
} var stValue = table[name] as string;
if (stValue != null)
{
if (!keys.Contains(name))
{
keys.Add(name);
parameters.Add(new KeyValuePair<string, object>(name, table[name]));
} return string.Concat(prefix, name);
} var value = table[name] as IEnumerable;
if (value != null)
{
int totalCount = ;
var ator = value.GetEnumerator();
var newNameList = new List<string>();
while (ator.MoveNext())
{
totalCount++;
var newkey = string.Format("{0}{1}x{2}z", prefix, name, totalCount);
newNameList.Add(newkey);
parameters.Add(new KeyValuePair<string, object>(newkey, ator.Current));
} return string.Concat(string.Join(",", newNameList.ToArray()));
} if (!keys.Contains(name))
{
keys.Add(name);
parameters.Add(new KeyValuePair<string, object>(name, table[name]));
} return string.Concat(prefix, name);
}); this.builded = true;
keys.Clear();
return this;
}
}

通过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)这三个参数。

 //注意是string,实现了IEnumerable接口
var stValue = table[name] as string;
if (stValue != null)
{
if (!keys.Contains(name))
{
keys.Add(name);
parameters.Add(new KeyValuePair<string, object>(name, table[name]));
} return string.Concat(prefix, name);
} var value = table[name] as IEnumerable;
if (value != null)
{
int totalCount = ;
var ator = value.GetEnumerator();
var newNameList = new List<string>();
while (ator.MoveNext())
{
totalCount++;
var newkey = string.Format("{0}{1}x{2}z", prefix, name, totalCount);
newNameList.Add(newkey);
parameters.Add(new KeyValuePair<string, object>(newkey, ator.Current));
} return string.Concat(string.Join(",", newNameList.ToArray()));
}

可以知道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>方法进行分析

 /// <summary>
/// 查询列表
/// </summary>
/// <typeparam name="T">返回对象类型</typeparam>
/// <param name="command">查询命令</param>
/// <param name="closeConnection">关闭数据库连接</param>
/// <returns></returns>
protected virtual T QueryForObject<T>(IDbCommand command, bool closeConnection)
{
var @delegate = DataRecordBuilder<T>.Func;
IDataReader reader = null;
try
{
using (reader = this.CreateReader(command))
{
var rd = new IDataRecordDecorator(reader);
if (reader.Read())
{
return @delegate(rd.Load(reader));
}
}
}
catch
{
throw;
}
finally
{
if (this.Transaction == null && closeConnection && reader != null && !reader.IsClosed)
reader.Close();
} return default(T);
}

看到这一行代码,是一个Func的委托

 var @delegate = DataRecordBuilder<T>.Func;

我们定位到DataRecordBuilder<T>这一个类

 /// <summary>
/// 对对象进行emit操作
/// </summary>
/// <param name="emit">The emit.</param>
public static void BuildObject(EasyEmitBuilder<Func<IDataRecord, T>> emit)
{
var type = typeof(T);
var targetMembers = GetMembers(type); /*实例*/
var instanceLocal = emit.DeclareLocal(type);
if (type.IsValueType)
{
if (targetMembers == null || targetMembers.Count == )
{
emit.LoadLocalAddress(instanceLocal);
emit.InitializeObject(type);
emit.LoadLocal(instanceLocal);
emit.Return();
return;
} emit.LoadLocalAddress(instanceLocal);
emit.InitializeObject(type);
emit.LoadLocal(instanceLocal);
emit.StoreLocal(instanceLocal);
goto _Read;
}
}

可以看到,里面是使用emit技术实现对对象T的属性或字段进行读写与赋值的(emit是后面所有技术点的基础)。

TypeHandler,有时候阻抗失败使用的方式,还是在DataRecordBuilder<T>这一个类中,我们拿属性进行get或set的时候,会对该属性或字段进行查询TypeHandlerAttribute这个attribute,

 var attribute = member.GetCustomAttribute<TypeHandlerAttribute>();

那么是怎么用呢?拿demo一个Typehander例子来看其使用

 public class User
{
public int Id { get; set; } public long UserId { get; set; } [Never.SqlClient.TypeHandler(typeof(UserNameTypeHandler))]
public char[] UserName { get; set; }
} public class UserNameTypeHandler : IReadingFromDataRecordToValueTypeHandler<char[]>, ICastingValueToParameterTypeHandler<string>
{
/// <summary>
///
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public string ToParameter(object value)
{
if (value == null)
return string.Empty; return new string((char[])value);
} /// <summary>
/// 获取结果
/// </summary>
/// <param name="dataRecord">读取器</param>
/// <param name="ordinal">column的位置,如果未-1表示没有找到这个值</param>
/// <param name="columnName">行名字</param>
/// <returns></returns>
public char[] ToValue(IDataRecord dataRecord, int ordinal, string columnName)
{
var value = dataRecord.GetString(ordinal);
return value == null ? new char[] : value.ToCharArray();
}
}

这个UserNameTypeHandler对象实现了2个接口,一个是从数据库到程序的类型转换IReadingFromDataRecordToValueTypeHandler<char[]>,一个是将程序的char[]类型换成数据库的string类型ICastingValueToParameterTypeHandler<string>,而User对象的UserName属性恰好也是char[]类型。

总结:组件使用emit实现核心方法,性能不底;并且有TypeHandler的支持,应该来说大部分数据结构应该还是可以应付的。本人没有测试过图片字段的,所以在对binary等字段处理,我想可以通过TypeHandler来实现

文章导航:

  1. never框架
  2. easySql使用xml管理带事务的orm
  3. ioc工具easyioc

never下sqlcient的更多相关文章

  1. C++程序结构---1

    C++ 基础教程Beta 版 原作:Juan Soulié 翻译:Jing Xu (aqua) 英文原版 本教程根据Juan Soulie的英文版C++教程翻译并改编. 本版为最新校对版,尚未定稿.如 ...

  2. never下ioc

    生命周期 当前分单例,作用域(范围),短暂.单例是整个服务中只有一个实例,短暂则是每一次得到的都是新的实例,作用域就是在该一套行动中内得到的是同一个实例,该行动中指的是什么?我们看看demo下的sta ...

  3. never下的easysql

    什么是EasySql 在我们早期写的代码中,想实现组装灵活的sql语句与参数,我们可以去翻阅早期自己写的代码 var @sb = new StringBuilder(); sb.Append(&quo ...

  4. Android SwipeRefreshLayout 下拉刷新——Hi_博客 Android App 开发笔记

    以前写下拉刷新 感觉好费劲,要判断ListView是否滚到顶部,还要加载头布局,还要控制 头布局的状态,等等一大堆.感觉麻烦死了.今天学习了SwipeRefreshLayout 的用法,来分享一下,有 ...

  5. IE6、7下html标签间存在空白符,导致渲染后占用多余空白位置的原因及解决方法

    直接上图:原因:该div包含的内容是靠后台进行print操作,输出的.如果没有输出任何内容,浏览器会默认给该空白区域添加空白符.在IE6.7下,浏览器解析渲染时,会认为空白符也是占位置的,默认其具有字 ...

  6. Ubuntu下使用nvm

    写在前面:刚写着写着博客就跨年了,希望新的一年大家万事如意,一切向"前"看! 安装 wget -qO- https://raw.githubusercontent.com/crea ...

  7. Cmder--Windows下命令行利器

    cmder cmder是一个增强型命令行工具,不仅可以使用windows下的所有命令,更爽的是可以使用linux的命令,shell命令. 安装包 安装包链接 下载后,直接解压即用. 修改命令提示符λ为 ...

  8. NodeJs在Linux下使用的各种问题

    环境:ubuntu16.04 ubuntu中安装NodeJs 通过apt-get命令安装后发现只能使用nodejs,而没有node命令 如果想避免这种情况请看下面连接的这种安装方式: 拓展见:Linu ...

  9. GreenDao 数据库:使用Raw文件夹下的数据库文件以及数据库升级

    一.使用Raw文件夹下的数据库文件 在使用GreenDao框架时,数据库和数据表都是根据生成的框架代码来自动创建的,从生成的DaoMaster中的OpenHelper类可以看出: public sta ...

随机推荐

  1. Like关系查询

    比如:有表1.表2两张相,希望通过like进行关联查询 // mysql中使用concat连接字符串 select  t1.id, t1.title, t2.keyword from t1 inner ...

  2. Python 标准库 —— string

    1. maketrans()/translate() maketrans(frm, to) -> string, 建立从字符串 frm 到 to 的映射表(字符串的形式): translate( ...

  3. 数据中台解析Hive SQL过程

    一.数据中台解析SQL的目的: 数据中台需要对外提供数据特征查询的能力,因此中台查找并解析各个平台的sql,找出哪些表中的字段经常被使用,以便沉淀为特征,而我们要做的是找出sql中的数据表及其字段.以 ...

  4. 矩阵十点【两】 poj 1575 Tr A poj 3233 Matrix Power Series

    poj 1575  Tr A 主题链接:http://acm.hdu.edu.cn/showproblem.php?pid=1575 题目大意:A为一个方阵,则Tr A表示A的迹(就是主对角线上各项的 ...

  5. uwp - 解决“Microsoft.EntityFrameworkCore.Tools –Pre因为在此系统上禁止运行脚本”

    在uwp使用ef时,需要安装“Microsoft.EntityFrameworkCore.Tools –Pre” ,如果安装失败提示:“无法加载文件 \.nuget\packages\Microsof ...

  6. PMP项目经理认证

    PMP认证是由美国项目管理学会(PMI)在全球范围内推出的针对项目经理的资格认证体系,通过该认证的项目经理叫"PMP",即Project Management Profession ...

  7. 大约PCA算法学习总结

    文章来源:http://blog.csdn.net/xizhibei ============================= PCA,也就是说,PrincipalComponents Analys ...

  8. springboot 使用日志

    spring boot 默认使用日志打印到console 添加application.properties文件在src/main/resoures文件夹下 logging.file=my.log 将日 ...

  9. mysql 优化 读写分离 主从复制

    1:mysql所在服务器内核 优化  ---------此优化可由系统运维人员完成 2:mysql配置参数优化(my.cnf) -------- 此优化需进行压力测试来进行参数调整 3:sql语句及表 ...

  10. 在vs2015上使用asp.net core+ef core

    官方的文档https://docs.asp.net/en/latest/tutorials/first-mvc-app/start-mvc.html 先来看一下实现的效果