最近在研究Dapper Source Code想要抽离一些部分封装自己想要的SQLHelper
做一些简单笔记整理分享给版友,假如有不足的地方也期待版友留言、讨论
使用建议
当select结构是select top 1 * 形式,建议使用QueryFirstOrDefault当用到LINQ FirstOrDefault也建议使用『 QueryFirstOrDefault + 修改SQL为select top 1 』原因效能快,QueryFirstOrDefault跟Query的效率比较参考,两者在测试比较是87.80 us比94.05 us。
connection.Query("select * from T").FirstOrDefault();connection.Query("select top 1 * from T");改为connection.QueryFirstOrDefault("select top 1 * from T");
至于要如何手写一个Dapper QueryFirstOrDefault核心Core
我写一个简化版本(不实现Mapping)给读者参考:
有几个重点
一定要在 ExecuteReader使用CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow
行为,这意思是告诉资料库请按顺序给我第一列资料就好
,这是提高效率关键请使用单次read
读取第一行资料资料,并使用空动作while把资料流快速读完
(可以参考底下详细说明)因为藉由IDbConnection介面呼叫建立方法建立command跟reader物件,支持多资料库使用泛型类别判断,以Func返回类别当结果类别依据,达到强型别维护using System.Data;using System.Data.Common;public static class DapperDemo{public static T QueryFirstOrDefault<T>(this IDbConnection connection,string sql,Func<IDataRecord,T> selector){using (var cmd = connection.CreateCommand()){cmd.CommandText = sql;using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow)){var data = (reader.Read() && reader.FieldCount != 0) ? selector(reader) : default(T);while (reader.Read()) { }while (reader.NextResult()) { }return data;}}}}
最后做测试Demo
void Main(){using(var conn = Connection){conn.Open();//Test Datavar cmd = conn.CreateCommand();cmd.CommandText = "with cte as ( select '0001' id,'ITWeiHan' name union all select '0002' id,'Henry' name ) select * into #T from cte ;";cmd.ExecuteNonQuery();//Queryvar result = conn.QueryFirstOrDefault("select top 1 * from #T", reader => new {id=reader.GetString(0),name=reader.GetString(1)});}}
感谢米欧问题:
资料流没有读完就 return,会发生什么事情?
可以参考Dapper作者回应的ISSUE: Issue #1210 · StackExchange/Dapper
主要避免忽略错误,像是在DataReader提早关闭情况,并且前面已经跟系统强调行为是单列资料(CommandBehavior.SingleRow,CommandBehavior.SingleResult),所以不会增加多少成本。
跟S.O的DataReader底层介绍 c# - How DataReader works? - Stack Overflow