本文内容特点性能参数化的查询List 支持缓存和非缓存的 readers多个映射多个结果存储过程Ansi Strings 和 varchar限制和注意事项Dapper 能运行在我的 db 提供者上吗?有例子的完整例子列表吗?谁在使用 Dapper?参考跳槽了,新公司的数据库层,准确地说,数据库层和持久层使用 Dapper,这东西的确很方便~个人觉得这种方便性体现在三点:
能很方便地执行数据库 DML 和 DLL 操作。比如,当你执行一个带参数的 SQL 时,SQL 中的变量能与你传递给它的实体或匿名对象中的属性,自定匹配。而我们知道,带参数的 SQL,能提高数据库执行 SQL 的效率;能很方便地将数据库检索结果映射为面向对象的对象。从数据库中的检索结果,通常是张二维表,如 DataTable,而应用程序中是实体类,以及实体类的集合,那么 Dapper 能够将 DataTable 自动地映射成为实体类的集合;能很方便地书写 SQL 语句。比如,写多个 SQL,用分号分隔。下载 Demo(该下载包含 Dapper 项目,项目中有 Dapper 的测试示例和性能测试例子)(Dapper 的示例使用 SQLServer 数据库,我个人的示例是MySQL)特点Dapper 只有一个文件,你可以把它拖到你的项目中,来扩展你的IDbConnection接口。
它提供了三方面的帮助:
执行一个查询,并把结果映射到一个强类型 list注意:所有的扩展方法都假设数据库连接已打开,如果关闭连接,它们将失败。
publicstaticIEnumerable<T> Query<T>(thisIDbConnection cnn,stringsql,objectparam =null, SqlTransaction transaction =null,boolbuffered =true)
用法:
publicclassDog
{
publicint? Age { get; set; }
publicGuid Id { get; set; }
publicstringName { get; set; }
publicfloat? Weight { get; set; }
publicintIgnoredPRoperty { get {return1; } }
}
var guid = Guid.NewGuid();
var dog = connection.Query<Dog>("select Age = @Age, Id = @Id",new{ Age = (int?)null, Id = guid });
dog.Count()
.IsEqualTo(1);
dog.First().Age
.IsNull();
dog.First().Id
.IsEqualTo(guid);
执行一个查询,并把结果映射到一个动态 object 的 listpublicstaticIEnumerable<dynamic> Query (thisIDbConnection cnn,stringsql,objectparam =null, SqlTransaction transaction =null,boolbuffered =true)
该方法将执行 SQL,并返回一个动态 list,即var变量。
用法:
var rows = connection.Query("select 1 A, 2 B union all select 3, 4");
((int)rows[0].A)
.IsEqualTo(1);
((int)rows[0].B)
.IsEqualTo(2);
((int)rows[1].A)
.IsEqualTo(3);
((int)rows[1].B)
.IsEqualTo(4);
rows[0]这种访问方式会出错,不知道示例是怎么给的~
执行一个不返回结果的 CommandpublicstaticintExecute(thisIDbConnection cnn,stringsql,objectparam =null, SqlTransaction transaction =null)
用法:
connection.Execute(@"
set nocount on
create table #t(i int)
set nocount off
insert #t
select @a a union all select @b
set nocount on
drop table #t",new{a=1, b=2 })
.IsEqualTo(2);
多次执行一个 Command相同的签名也可以让你方便高效地对一个命令执行多次,例如批量加载数据(bulk-load data)。
用法:
connection.Execute(@"insert MyTable(colA, colB) values (@a, @b)",
new[] {new{ a=1, b=1 },new{ a=2, b=2 },new{ a=3, b=3 } }
).IsEqualTo(3); // 3 rows inserted:"1,1","2,2"and"3,3"
对任何类型实现T为IEnumerable的参数都可以执行。
性能Dapper 的主要特点是性能。以下数据显示对一个数据库执行 SELECT 出 500 条,并把数据映射到对象中需要多长时间。
性能测试分为三个方面:
POCO 序列化框架,支持从数据库获得静态类型的对象。使用原始的 SQL。动态序列化框架,支持返回对象的动态列表。典型的框架用法。往往不会涉及编写 SQL。Performance of SELECT mapping over 500 iterations - POCO 序列化方法
持续时间(毫秒)
备注
Hand coded (using a SqlDataReader)
47
Can be faster
Dapper ExecuteMapperQuery
49
同上
ServiceStack.OrmLite (QueryById)
50
同上
PetaPoco
52
同上
BLToolkit
80
同上
SubSonic CodingHorror
107
同上
NHibernate SQL
104
同上
Linq 2 SQL ExecuteQuery
181
同上
Entity framework ExecuteStoreQuery
631
同上
Performance of SELECT mapping over 500 iterations - dynamic 序列化方法
持续时间(毫秒)
备注
Dapper ExecuteMapperQuery (dynamic)
48
Massive
52
Simple.Data
95
Performance of SELECT mapping over 500 iterations - 典型用法方法
持续时间(毫秒)
备注
Linq 2 SQL CompiledQuery
81
Not super typical involves complex code
NHibernate HQL
118
Linq 2 SQL
559
Entity framework
859
SubSonic ActiveRecord.SingleOrDefault
3619
参数化的查询参数可以作为匿名类来传递。这使你可以轻松地给命名参数,只要简单地剪切和粘贴 SQL 片断,并在查询分析器中执行即可。
new{A = 1, B ="b"} // A will be mapped to the param @A, B to the param @B
List 支持Dapper 运行让你传递IEnumerable,自动地参数化的查询。
例如下面 SQL 的in查询:
connection.Query<int>("select * from (select 1 as Id union all select 2 union all select 3) as X where Id in @Ids",new{ Ids =newint[] { 1, 2, 3 });
将被翻译为:
select * from (select 1asId union all select 2 union all select 3)asXwhereIdin(@Ids1, @Ids2, @Ids3)" // @Ids1 = 1 , @Ids2 = 2 , @Ids2 = 3
缓存和非缓存的 readersDapper 的默认动作是执行 SQL 并在返回时缓冲整个 reader。在大多数情况下,这是理想的,因为它能最大限度地减少数据库中的共享锁,以及减少数据库的网络时间。
但是,在执行庞大查询时,你可能为了减少内存的占用,只加载需要的对象。要做到这点,通过缓冲到查询方法中。
/// <summary>
/// NonBuffered
/// 将会抛出异常,Invalid attempt to Read when reader is closed.
/// </summary>
publicstaticvoidTestBasicStringUsageAsyncNonBuffered()
{
var query = DbHelp.QueryAsync<string>(newCommandDefinition("select 'abc' as Value union all select @txt",new{ txt ="def"}, flags: CommandFlags.None));
var arr = query.Result.ToArray();
arr.IsSequenceEqualTo(new[] {"abc","def"});
}
/// <summary>
/// Buffered
/// 不会抛出异常
/// </summary>
publicstaticvoidTestBasicStringUsageAsyncBuffered()
{
var query = DbHelp.QueryAsync<string>(newCommandDefinition("select 'abc' as Value union all select @txt",new{ txt ="def"}, flags: CommandFlags.Buffered));
var arr = query.Result.ToArray();
arr.IsSequenceEqualTo(new[] {"abc","def"});
}
/// <summary>
/// Pipelined
/// 将会抛出异常,Invalid attempt to Read when reader is closed.
/// </summary>
publicstaticvoidTestBasicStringUsageAsyncPipelined()
{
var query = DbHelp.QueryAsync<string>(newCommandDefinition("select 'abc' as Value union all select @txt",new{ txt ="def"}, flags: CommandFlags.Pipelined));
var arr = query.Result.ToArray();
arr.IsSequenceEqualTo(new[] {"abc","def"});
}
多个映射Dapper 允许你把单行映射到多个对象。如果你想避免额外的查询和加载关联,那么这个功能就很关键了。
例如:
var sql =
@"select * from #Posts p
left join #Users u on u.Id = p.OwnerId
Order by p.Id";
var data = connection.Query<Post, User, Post>(sql, (post, user) => { post.Owner = user;returnpost;});
var post = data.First();
post.Content.IsEqualTo("Sams Post1");
post.Id.IsEqualTo(1);
post.Owner.Name.IsEqualTo("Sam");
post.Owner.Id.IsEqualTo(99);
提示:Dapper 假定你的 ID 列被命名为“ID”或“id”,如果你的主键是不同的,或者你想在点上拆分宽行点,而不是“ID”,可以使用可选的'splitOn'参数。
多个结果Dapper 允许你在一次查询中处理多个结果的集合。
例如:
var sql =
@"
select * from Customers where CustomerId = @id
select * from Orders where CustomerId = @id
select * from Returns where CustomerId = @id";
using(var multi = connection.QueryMultiple(sql,new{id=selectedId}))
{
var customer = multi.Read<Customer>().Single();
var orders = multi.Read<Order>().ToList();
var returns = multi.Read<Return>().ToList();
...
}
存储过程Dapper 完全支持存储过程:
var user = cnn.Query<User>("spGetUser",new{Id = 1},
commandType: CommandType.StoredProcedure).First();}}}
如果你想要更灵活的操作,可以这样做:
var p =newDynamicParameters();
p.Add("@a", 11);
p.Add("@b", dbType: DbType.Int32, direction: ParameterDirection.Output);
p.Add("@c", dbType: DbType.Int32, direction: ParameterDirection.ReturnValue);
cnn.Execute("spMagicProc", p, commandType: CommandType.StoredProcedure);
intb = p.Get<int>("@b");
intc = p.Get<int>("@c");
Ansi Strings 和 varchar
Dapper 支持 varchar 参数,如果你在一个 varchar 列上执行一个 where 语句,确保下面方式传递参数:
Query<Thing>("select * from Thing where Name = @Name",new{Name =newDbString { Value ="abcde", IsFixedLength =true, Length = 10, IsAnsi =true});
在 SQL Server 上,当查询非 Unicode 时,查询 Unicode 和 ANSI 时需要使用 Unicode。
限制和注意事项对于 Dapper 执行的每个查询的缓存信息,使得它能够快速地物化对象和处理参数。当前的实现把信息缓存在一个 ConcurrentDictionary 对象中。它存储的对象永远不会被刷新。如果你生成的 SQL 字符串没有使用参数,那么可能会出现命中内存问题。我们把字典转换成 LRU(Least Recently Used)缓存。
ORM 的很多特点都被 Dapper 去掉了,没有身份地图(IdentityMap),没有更新/选择的助手等。
Dapper 不会管理你连接的生命周期,它假定它得到的连接是打开的,并且不存在 DataReader 枚举(除非启用 MARS)。
什么是 Mars?它是在创建数据库连接时指定的,下面是 Dapper 中连接 SQL Server 的示例:
publicstaticSqlConnection GetOpenConnection(boolmars =false)
{
var cs = connectionString;
if(mars)
{
SqlConnectionStringBuilder scsb =newSqlConnectionStringBuilder(cs);
scsb.MultipleActiveResultSets =true;
cs = scsb.ConnectionString;
}
var connection =newSqlConnection(cs);
connection.Open();
returnconnection;
}
如果指定了 Mars,那么还会创建SqlConnectionStringBuilder,并指定其MultipleActiveResultSets属性为true。不过,看 Dapper 的例子,貌似 SQL Server 是有 Mars 的,但 MySQL 没有。
Dapper 能运行在我的 db 提供者上吗?Dapper 能运行在所有 .net ado 提供者上,包括 sqlite,sqlce,firebird,Oracle,MySQL,PostgreSQL 和 SQL Server。
有例子的完整例子列表吗?Dapper 在测试项目中有完整的测试套件。
谁在使用 Dapper?目前使用 Dapper 的有 Stack Overflow 和 helpdesk。
(if you would like to be listed here let me know)
参考资料github Dapperstackoverflow DapperA Look at Dapper.NET
下载 Demo