本文源自: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的RawDataAccessBencherOrmBenchmark测试套件作为测试工具使用。

参数化查询

可以匿名类型作为参数进行传递,这可以轻松地命名这些参数名称,且能够在数据库平台的查询分析器中简单地使用剪切、粘贴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对象,然后给它描述你想要对包含PostUser对象的每一行做什么的函数。 在我们的例子中,我们想要获取用户对象并将其放在post对象中。所以编写函数如下:

(post, user) => { post.Owner = user; return post; }

Query方法的3个类型参数指定dapper应该使用哪些对象及返回的内容进行反序列化行。我们将把这两行解释为PostUser的组合,然后我们返回一个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”的数据库表,其中包含列:IdTypeData,你想要基于Type列的值将它的行解析为CircleSquareTriangle对象。

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)的更多相关文章

  1. git使用教程2-更新github上代码

    前面一篇已经实现首次上传代码到github了,迈出了装逼第一步,本篇继续讲如何把本地更新的代码同步更新到github上 一.clone代码 1.把大神的代码clone到本地,或者clone自己gith ...

  2. git使用教程2-更新github上代码【转载】

    本篇转自博客:上海-悠悠 原文地址:http://www.cnblogs.com/yoyoketang/tag/git/ 前言 前面一篇已经实现首次上传代码到github了,迈出了装逼第一步,本篇继续 ...

  3. 文科妹学 GitHub 简易教程(转)

    文科妹学 GitHub 简易教程     #什么是 Github ?必须要放这张图了!!! Git 是由 Linux 之父 Linus Tovalds 为了更好地管理linux内核开发而创立的分布式版 ...

  4. Ocelot简易教程(六)之重写配置文件存储方式并优化响应数据

    本来这篇文章在昨天晚上就能发布的,悲剧的是写了两三千字的文章居然没保存,结果我懵逼了.今天重新来写这篇文章.今天我们就一起来探讨下如何重写Ocelot配置文件的存储方式以及获取方式. 作者:依乐祝 原 ...

  5. [转载]Ocelot简易教程(六)之重写配置文件存储方式并优化响应数据

    作者:依乐祝 原文地址:https://www.cnblogs.com/yilezhu/p/9807125.html 很多人都说配置文件的配置很繁琐,如果存储在数据库就方便很多,可以通过自定义UI界面 ...

  6. github上最全的资源教程-前端涉及的所有知识体系

    前面分享了前端入门资源汇总,今天分享下前端所有的知识体系. 个人站长对个人综合素质要求还是比较高的,要想打造多拉斯自媒体网站,不花点心血是很难成功的,学习前端是必不可少的一个环节, 当然你不一定要成为 ...

  7. 【转载】【收藏】Github上免费的编程教程【作者Victor Felder】

    原链接:https://github.com/EbookFoundation/free-programming-books/blob/master/free-programming-books-zh. ...

  8. github上最全的资源教程-前端涉及的所有知识体系【转】

    github上最全的资源教程-前端涉及的所有知识体系[转自:蓝猫的博客] 综合类 综合类 地址 前端知识体系 http://www.cnblogs.com/sb19871023/p/3894452.h ...

  9. Docker之Ubuntu上使用Docker的简易教程

    Ubuntu上使用Docker的简易教程 原始文档:https://www.yuque.com/lart/linux/fp6cla 说在开头 在天池的比赛中涉及到了docker的使用.经过多番探究,大 ...

随机推荐

  1. RecyclerView 高度不能随着Item数量 自适应高度

    在最近项目中遇到 ,在RecyclerView加载list数据时,高度无法自适应增长,看了很多博客,各种尝试,都没有解决这个问题,在某个博客中,讲到此解决方法,在此记录下. 即在RecyclerVie ...

  2. Linux 网卡聚合

    Linux 网卡聚合的类型: 1.broadcast:传输来自所有端口的每个包 2.roundrobin:以轮播方式传输来自每个端口的包 3.activebackup:故障转移运行程序,监视更改并选择 ...

  3. 通过linkserver不能调远程表值函数

    Question: 通过linkserver调远程表值函数报错如下 Solution: 注意:查询语句中的[SDS_NONEDI].[DBO].ddddd(),不能加server名[sdsc2-1]. ...

  4. Linux内存描述之概述--Linux内存管理(一)

    1 前景回顾 1.1 UMA和NUMA两种模型 共享存储型多处理机有两种模型 均匀存储器存取(Uniform-Memory-Access,简称UMA)模型 将可用内存以连续方式组织起来, 非均匀存储器 ...

  5. C# -- Lambda 表达式的使用

    C# -- Lambda 表达式的使用 Lambda 表达式是作为对象处理的代码块(表达式或语句块). 它可作为参数传递给方法,也可通过方法调用返回. Lambda 表达式是可以表示为委托的代码,或者 ...

  6. ASP.NET -- WebForm -- ViewState

    ASP.NET -- WebForm --  ViewState 1. ViewState的作用 当 ASP .NET 中的表单被提交时,表单会随所有表单值一同重新出现.这是由于 ASP .NET 维 ...

  7. Angular四大核心特性

    Angular四大核心特性 Angular四大核心特性理论概述 MVC模式:它目的是为了分离视图.模型和控制器而设计出来的:其中数据模型用来储存数据,视图用来向用户展示应用程序,控制器充当模型和视图之 ...

  8. 《Java大学教程》—第24章 Java的背景

    本章主要介绍的是Java的背景知识,通过了解历史知道Java与其他语言的区别,以便更好选择在什么场景下使用Java. 24.2    语言的尺寸Java语言短小.紧凑,以C++为基础,放弃了一些特定的 ...

  9. winfrom之datagridview分页显示

    这次datagridview绑定数据并分页操作,因为用到了webservice,所以代码会详细讲解.QueryByCondition是一个查询函数 客户端: PageData pageData=new ...

  10. A - Packets 贪心

    A factory produces products packed in square packets of the same height h and of the sizes 1*1, 2*2, ...