Dapper简易教程(翻译自Github上StackExchange/Dapper)
本文源自:https://github.com/cnxy/Dapper-zh-cn
本博客作者与Github上作者(cnxy)实为同一个作者。由于笔者翻译水平有限,文本中错误难免,欢迎指正!
本文翻译自:StackExchange.Dapper
原版教程源自:Dapper Tutorial
中文教程源自:中文Dapper教程.GitBook
中文教程PDF:dapper-tutorial-cn
Dapper - .Net版本的简单对象映射器
发行说明
请见 stackexchange.github.io/Dapper
组件
Nuget稳定版:
https://www.nuget.org/packages/Dapper
Visual Studio 程序包管理器控制台:
PM> Install-Package Dappe
特点
Dapper是一个NuGet库,您可以将其添加到项目中,以扩展您的IDbConnection
接口。
它提供了3个使用方法:
执行一个查询并将结果映射到强类型列表
public static IEnumerable<T> Query<T>(this IDbConnection cnn, string sql, object param = null, SqlTransaction transaction = null, bool buffered = true)
示例:
public class Dog
{
public int? Age { get; set; }
public Guid Id { get; set; }
public string Name { get; set; }
public float? Weight { get; set; } public int IgnoredProperty { get { return ; } }
} var guid = Guid.NewGuid();
var dog = connection.Query<Dog>("select Age = @Age, Id = @Id", new { Age = (int?)null, Id = guid }); Assert.Equal(,dog.Count());
Assert.Null(dog.First().Age);
Assert.Equal(guid, dog.First().Id);
执行一个查询并将其映射到动态对象列表
public static IEnumerable<dynamic> Query (this IDbConnection cnn, string sql, object param = null, SqlTransaction transaction = null, bool buffered = true)
这个方法会执行SQL语句,并返回一个动态列表。
示例:
var rows = connection.Query("select 1 A, 2 B union all select 3, 4"); Assert.Equal(, (int)rows[].A);
Assert.Equal(, (int)rows[].B);
Assert.Equal(, (int)rows[].A);
Assert.Equal(, (int)rows[].B);
执行不返回结果的命令
public static int Execute(this IDbConnection cnn, string sql, object param = null, SqlTransaction transaction = null)
示例:
var count = 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=, b= });
Assert.Equal(, count);
多次执行命令
还允许使用相同的参数签名方便有效地多次执行命令(例如批量加载数据)
示例:
var count = connection.Execute(@"insert MyTable(colA, colB) values (@a, @b)",
new[] { new { a=, b= }, new { a=, b= }, new { a=, b= } }
);
Assert.Equal(, count); // 插入3行: "1,1", "2,2" 与 "3,3"
这适用于已经实现IEnumerable接口的集合对象T。
性能
Dapper的一个关键特性是性能。 以下度量标准显示了对DB执行500个SELECT
语句并将返回的数据映射到对象所需的时间。
性能测试分为3个列表:
- 支持从数据库中提取静态类型对象框架的POCO序列化,使用原生SQL语句。
- 支持返回动态对象列表框架的动态序列化。
- 典型的框架用法:通常典型的框架使用与最佳使用性能明显不同,并且它不会涉及编写SQL语句。
超过500次迭代的SELECT映射性能 - POCO序列化
方法 | 执行时间 | 备注 |
---|---|---|
手工编码 (使用 SqlDataReader ) |
47ms | |
Dapper ExecuteMapperQuery |
49ms | |
ServiceStack.OrmLite (使用Id查询) | 50ms | |
PetaPoco | 52ms | 可以更快 |
BLToolkit | 80ms | |
SubSonic CodingHorror | 107ms | |
NHibernate SQL | 104ms | |
Linq 2 SQL ExecuteQuery |
181ms | |
Entity framework ExecuteStoreQuery |
631ms |
超过500次迭代的SELECT映射性能 - 动态序列化
方法 | 执行时间 | 备注 |
---|---|---|
Dapper ExecuteMapperQuery (动态) |
48ms | |
Massive | 52ms | |
Simple.Data | 95ms |
超过500次迭代的SELECT映射性能 - 典型用法
方法 | 执行时间 | 备注 |
---|---|---|
Linq 2 SQL CompiledQuery | 81ms | 非典型的且不涉及复杂的代码 |
NHibernate HQL | 118ms | |
Linq 2 SQL | 559ms | |
Entity framework | 859ms | |
SubSonic ActiveRecord.SingleOrDefault | 3619ms |
性能基准测试信息 点击这里.
可以任意提交包含其他ORM的补丁 - 运行基准测试时,请确保在Release中编译,且不能附加调试器 (Ctrl+F5).
或者,你可以使用Frans Bouma的RawDataAccessBencher或OrmBenchmark测试套件作为测试工具使用。
参数化查询
可以匿名类型作为参数进行传递,这可以轻松地命名这些参数名称,且能够在数据库平台的查询分析器中简单地使用剪切、粘贴SQL语句并运行。
new {A = , B = "b"} // A映射到参数@A,B映射到参数@B
列表支持
Dapper允许将IEnumerable<int>
作为传递参数,并能够自动地参数化查询
例子:
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 = new int[] { , , } });
以上将被转换成:
select * from (select 1 as Id union all select 2 union all select 3) as X where Id in (@Ids1, @Ids2, @Ids3)" // @Ids1 = 1 , @Ids2 = 2 , @Ids2 = 3
文字代替
Dapper支持布尔与数字类型的文字代替。
connection.Query("select * from User where UserId = {=Id}", new {Id = }));
文字替换不作为参数发送; 更好的计划和过滤索引用法将被允许,但通常应谨慎在测试后使用。 当注入的值实际上是固定值(例如,特定于查询的“类别ID”,“状态代码”或“区域”)时,此功能特别有用。 当你在思考文字live数据时,也有可能想到also并测试特定于提供程序的查询提示,如带有常规参数的OPTIMIZE FOR UNKNOWN
。
缓冲与未缓冲阅读器
Dapper的默认行为是执行SQL并在返回时缓冲整个阅读器。 在大多数情况下,这是理想的,因为它最小化了数据库中的共享锁并减少了数据库网络时间。
但是,在执行大量查询时,可能需要最小化内存占用并仅根据需要加载对象。 为此,将buffered:false
传递给Query
方法。
多重映射
Dapper允许将单个行映射到多个对象。 如果想避免无关的查询和立即加载关联,这是一个很关键的特性。
例子:
思考这两个类: Post
and User
class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public User Owner { get; set; }
} class User
{
public int Id { get; set; }
public string Name { get; set; }
}
现在我们要把posts表单与users表单进行映射查询。到目前为止,如果我们需要结合2个查询的结果,我们需要一个新的对象来表达它,但在这种情况下将User
对象放在Post
对象中更有意义。
这是多重映射的用户案例。你告诉dapper查询返回一个Post
和一个User
对象,然后给它描述你想要对包含Post
和User
对象的每一行做什么的函数。 在我们的例子中,我们想要获取用户对象并将其放在post对象中。所以编写函数如下:
(post, user) => { post.Owner = user; return post; }
Query
方法的3个类型参数指定dapper应该使用哪些对象及返回的内容进行反序列化行。我们将把这两行解释为Post
和User
的组合,然后我们返回一个Post
对象。 因此类型声明变为
<Post, User, Post>
所有东西都放在一起,看起来像这样:
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; return post;});
var post = data.First(); Assert.Equal("Sams Post1", post.Content);
Assert.Equal(, post.Id);
Assert.Equal("Sam", post.Owner.Name);
Assert.Equal(, post.Owner.Id);
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 = },
commandType: CommandType.StoredProcedure).SingleOrDefault();
如果你想要更有趣的东西,你可以这样做:
var p = new DynamicParameters();
p.Add("@a", );
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); int b = p.Get<int>("@b");
int c = p.Get<int>("@c");
Ansi字符串和varchar
Dapper支持varchar参数,如果使用param在varchar列上执行where子句,请确保以这种方式传递它:
Query<Thing>("select * from Thing where Name = @Name", new {Name = new DbString { Value = "abcde", IsFixedLength = true, Length = , IsAnsi = true });
在SQL Server中,使用unicode编码查询unicode与ANSI编码或查询非unicode编码时,变得至关重要。
每行类型转换
通常,自己希望将给定表中的所有行视为相同的数据类型。 但是,在某些情况下,能够将不同的行解析为不同的数据类型是有用的。 这就是IDataReader.GetRowParser
派上用场的地方。
假设有一个名为“Shapes”的数据库表,其中包含列:Id
,Type
和Data
,你想要基于Type列的值将它的行解析为Circle
,Square
或Triangle
对象。
var shapes = new List<IShape>();
using (var reader = connection.ExecuteReader("select * from Shapes"))
{
// Generate a row parser for each type you expect.
// The generic type <IShape> is what the parser will return.
// The argument (typeof(*)) is the concrete type to parse.
var circleParser = reader.GetRowParser<IShape>(typeof(Circle));
var squareParser = reader.GetRowParser<IShape>(typeof(Square));
var triangleParser = reader.GetRowParser<IShape>(typeof(Triangle)); var typeColumnIndex = reader.GetOrdinal("Type"); while (reader.Read())
{
IShape shape;
var type = (ShapeType)reader.GetInt32(typeColumnIndex);
switch (type)
{
case ShapeType.Circle:
shape = circleParser(reader);
break;
case ShapeType.Square:
shape = squareParser(reader);
break;
case ShapeType.Triangle:
shape = triangleParser(reader);
break;
default:
throw new NotImplementedException();
} shapes.Add(shape);
}
}
限制与警告
Dapper缓存有关它运行的每个查询的信息,这使它能够快速实现对象并快速处理参数。 当前实现将此信息缓存在ConcurrentDictionary
对象中。仅使用一次的语句通常会从此缓存中刷新。尽管如此,如果您在不使用参数的情况下动态生成SQL字符串,则可能会遇到内存问题。
Dapper的简洁性意味着ORM附带的许多功能都被剥离了。Dapper担心95%的情况,并为您提供大多数时间所需的工具,并不试图解决所有问题。
Dapper支持哪些数据库?
Dapper没有特定于DB的实现细节,它适用于所有.NET ADO提供程序,包括SQLite(https://www.sqlite.org/),SQL CE,Firebird,Oracle,MySQL,PostgreSQL和SQL Server。
有完整的例子清单吗?
Dapper有一个完整位于测试工程的测试套件。
谁在用这个?
Stack Overflow正在使用Dapper。
Dapper简易教程(翻译自Github上StackExchange/Dapper)的更多相关文章
- git使用教程2-更新github上代码
前面一篇已经实现首次上传代码到github了,迈出了装逼第一步,本篇继续讲如何把本地更新的代码同步更新到github上 一.clone代码 1.把大神的代码clone到本地,或者clone自己gith ...
- git使用教程2-更新github上代码【转载】
本篇转自博客:上海-悠悠 原文地址:http://www.cnblogs.com/yoyoketang/tag/git/ 前言 前面一篇已经实现首次上传代码到github了,迈出了装逼第一步,本篇继续 ...
- 文科妹学 GitHub 简易教程(转)
文科妹学 GitHub 简易教程 #什么是 Github ?必须要放这张图了!!! Git 是由 Linux 之父 Linus Tovalds 为了更好地管理linux内核开发而创立的分布式版 ...
- Ocelot简易教程(六)之重写配置文件存储方式并优化响应数据
本来这篇文章在昨天晚上就能发布的,悲剧的是写了两三千字的文章居然没保存,结果我懵逼了.今天重新来写这篇文章.今天我们就一起来探讨下如何重写Ocelot配置文件的存储方式以及获取方式. 作者:依乐祝 原 ...
- [转载]Ocelot简易教程(六)之重写配置文件存储方式并优化响应数据
作者:依乐祝 原文地址:https://www.cnblogs.com/yilezhu/p/9807125.html 很多人都说配置文件的配置很繁琐,如果存储在数据库就方便很多,可以通过自定义UI界面 ...
- github上最全的资源教程-前端涉及的所有知识体系
前面分享了前端入门资源汇总,今天分享下前端所有的知识体系. 个人站长对个人综合素质要求还是比较高的,要想打造多拉斯自媒体网站,不花点心血是很难成功的,学习前端是必不可少的一个环节, 当然你不一定要成为 ...
- 【转载】【收藏】Github上免费的编程教程【作者Victor Felder】
原链接:https://github.com/EbookFoundation/free-programming-books/blob/master/free-programming-books-zh. ...
- github上最全的资源教程-前端涉及的所有知识体系【转】
github上最全的资源教程-前端涉及的所有知识体系[转自:蓝猫的博客] 综合类 综合类 地址 前端知识体系 http://www.cnblogs.com/sb19871023/p/3894452.h ...
- Docker之Ubuntu上使用Docker的简易教程
Ubuntu上使用Docker的简易教程 原始文档:https://www.yuque.com/lart/linux/fp6cla 说在开头 在天池的比赛中涉及到了docker的使用.经过多番探究,大 ...
随机推荐
- (办公)Spring boot(系列)的返回json封装类
package com.imooc.util; import com.fasterxml.jackson.databind.ObjectMapper; /** * 自定义响应数据结构: * 这个类是提 ...
- python编写文件统计脚本
python编写文件统计脚本 思路:用os模块中的一些函数(os.listdir().os.path.isdir().os.path.join().os.path.abspath()等) 实现功能:显 ...
- IIS下MySQL停止和启动的方法
mysql服务的启动与停止:点击开始--运行,输入services.msc , 在弹出的服务窗口中,找到mysql服务,直接点击左侧对应 的就可以了 如下图所示:
- Vue学习之路3-浅析Vue-cli搭建项目后的目录结构
1.前言 Vue并不限制你的代码结构.但是,它规定了一些需要遵守的规则:1.应用层级的状态应该集中到单个store对象中.2.提交mutation是更改状态的唯一方法,并且这个过程是同步的.3.异步逻 ...
- 【C编程基础】多线程编程
基础知识 1.基本概念 (1)线程,即轻量级进程(LWP:LightWeight Process),是程序执行流的最小单元. 线程是进程中的一个实体,是被系统独立调度和分派的基本单位. (2)线程同步 ...
- (转)Spring Boot 2 (二):Spring Boot 2 尝鲜-动态 Banner
http://www.ityouknow.com/springboot/2018/03/03/spring-boot-banner.html Spring Boot 2.0 提供了很多新特性,其中就有 ...
- C. Edgy Trees Codeforces Round #548 (Div. 2) 并查集求连通块
C. Edgy Trees time limit per test 2 seconds memory limit per test 256 megabytes input standard input ...
- 面试总结——Java篇
前言:前期对Java基础的相关知识点进行了总结,具体参看:Java基础和面试知识点.近期由于笔者正在换工作(ing),因此下面将笔者在面试过程中或笔者朋友面试过程中反馈的题目进行总结,相信弄清楚下面题 ...
- win10 搭建virtualenvwrapper虚拟环境
1. 安装virtualenvwrapper pip install virtualenvwrapper-win 注: linux下运行pip install virtualenvwrapper 2. ...
- 采用BitMap从20亿个int正整数中找出相同的数字
所谓的BitMap就是用一个bit位来标记某个元素所对应的value,而key即是该元素,由于BitMap使用了bit位来存储数据,因此可以大大节省存储空间. public class Test { ...