Dapper - a simple object mapper for .Net
Dapper - a simple object mapper for .Net
Release Notes
Located at stackexchange.github.io/Dapper
Packages
MyGet Pre-release feed: https://www.myget.org/gallery/dapper
Package | NuGet Stable | NuGet Pre-release | Downloads | MyGet |
---|---|---|---|---|
Dapper | ||||
Dapper.Contrib | ||||
Dapper.EntityFramework | ||||
Dapper.EntityFramework.StrongName | ||||
Dapper.Rainbow | ||||
Dapper.SqlBuilder | ||||
Dapper.StrongName |
Features
Dapper is a NuGet library that you can add in to your project that will extend your IDbConnection
interface.
It provides 3 helpers:
Execute a query and map the results to a strongly typed List
public static IEnumerable<T> Query<T>(this IDbConnection cnn, string sql, object param = null, SqlTransaction transaction = null, bool buffered = true)
Example usage:
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 1; } }
} var guid = Guid.NewGuid();
var dog = connection.Query<Dog>("select Age = @Age, Id = @Id", new { Age = (int?)null, Id = guid }); Assert.Equal(1,dog.Count());
Assert.Null(dog.First().Age);
Assert.Equal(guid, dog.First().Id);
Execute a query and map it to a list of dynamic objects
public static IEnumerable<dynamic> Query (this IDbConnection cnn, string sql, object param = null, SqlTransaction transaction = null, bool buffered = true)
This method will execute SQL and return a dynamic list.
Example usage:
var rows = connection.Query("select 1 A, 2 B union all select 3, 4"); Assert.Equal(1, (int)rows[0].A);
Assert.Equal(2, (int)rows[0].B);
Assert.Equal(3, (int)rows[1].A);
Assert.Equal(4, (int)rows[1].B);
Execute a Command that returns no results
public static int Execute(this IDbConnection cnn, string sql, object param = null, SqlTransaction transaction = null)
Example usage:
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=1, b=2 });
Assert.Equal(2, count);
Execute a Command multiple times
The same signature also allows you to conveniently and efficiently execute a command multiple times (for example to bulk-load data)
Example usage:
var count = 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 } }
);
Assert.Equal(3, count); // 3 rows inserted: "1,1", "2,2" and "3,3"
This works for any parameter that implements IEnumerable for some T.
Performance
A key feature of Dapper is performance. The following metrics show how long it takes to execute a SELECT
statement against a DB (in various config, each labeled) and map the data returned to objects.
The benchmarks can be found in Dapper.Tests.Performance (contributions welcome!) and can be run once compiled via:
Dapper.Tests.Performance.exe -f * --join
Output from the latest run is:
BenchmarkDotNet=v0.11.1, OS=Windows 10.0.17134.254 (1803/April2018Update/Redstone4)
Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
Frequency=2742188 Hz, Resolution=364.6723 ns, Timer=TSC
[Host] : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3163.0
ShortRun : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3163.0
ORM | Method | Return | Mean | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|
LINQ to DB | 'First (Compiled)' | Post | 78.75 us | 0.7500 | - | - | 2.66 KB |
LINQ to DB | Query<T> | Post | 80.38 us | 2.1250 | - | - | 6.87 KB |
Hand Coded | SqlCommand | Post | 87.16 us | 2.5000 | 1.0000 | 0.2500 | 12.24 KB |
Dapper | QueryFirstOrDefault<dynamic> | dynamic | 87.80 us | 4.3750 | - | - | 13.5 KB |
Belgrade | ExecuteReader | Post | 87.85 us | 3.6250 | 0.7500 | - | 11.27 KB |
Dapper | QueryFirstOrDefault<T> | Post | 91.51 us | 2.8750 | 0.8750 | 0.2500 | 13.46 KB |
Hand Coded | DataTable | dynamic | 91.74 us | 2.2500 | 0.6250 | - | 12.45 KB |
Dapper | 'Query<T> (buffered)' | Post | 94.05 us | 2.8750 | 0.8750 | 0.2500 | 13.79 KB |
Dapper | 'Query<dynamic> (buffered)' | dynamic | 95.25 us | 2.5000 | 1.0000 | 0.2500 | 13.87 KB |
Massive | 'Query (dynamic)' | dynamic | 96.18 us | 3.2500 | 0.8750 | 0.3750 | 14.19 KB |
PetaPoco | 'Fetch<T> (Fast)' | Post | 96.57 us | 2.7500 | 0.8750 | 0.2500 | 13.65 KB |
PetaPoco | Fetch<T> | Post | 97.62 us | 2.8750 | 0.8750 | 0.2500 | 14.59 KB |
Dapper | 'Contrib Get<T>' | Post | 98.85 us | 2.8750 | 1.0000 | 0.2500 | 14.45 KB |
ServiceStack | SingleById<T> | Post | 102.39 us | 3.1250 | 0.8750 | 0.3750 | 17.52 KB |
LINQ to DB | First | Post | 103.54 us | 1.7500 | - | - | 5.51 KB |
Susanoo | 'Execute<T> (Static)' | Post | 105.07 us | 2.8750 | 0.8750 | 0.2500 | 14.98 KB |
Dashing | Get | Post | 105.80 us | 3.1250 | 0.8750 | 0.3750 | 14.82 KB |
Susanoo | 'Execut<dynamic> (Static)' | dynamic | 109.26 us | 3.1250 | 0.8750 | 0.2500 | 14.97 KB |
LINQ to SQL | 'First (Compiled)' | Post | 114.62 us | 3.1250 | - | - | 9.82 KB |
Dapper | 'Query<T> (unbuffered)' | Post | 119.72 us | 3.1250 | 0.8750 | 0.2500 | 13.83 KB |
Susanoo | 'Execute<dynamic> (Cache)' | dynamic | 124.02 us | 3.6250 | 1.0000 | 0.5000 | 20.4 KB |
Susanoo | 'Execute<T> (Cache)' | Post | 126.92 us | 4.2500 | 1.0000 | 0.5000 | 20.88 KB |
Dapper | 'Query<dynamic> (unbuffered)' | dynamic | 139.89 us | 2.5000 | 1.0000 | 0.2500 | 13.87 KB |
EF 6 | SqlQuery | Post | 143.86 us | 5.2500 | 0.7500 | - | 27.86 KB |
EF Core | 'First (Compiled)' | Post | 148.42 us | 5.0000 | - | - | 16.08 KB |
NHibernate | Get<T> | Post | 196.88 us | 5.7500 | 1.0000 | - | 32.5 KB |
EF Core | First | Post | 197.91 us | 6.5000 | - | - | 20.25 KB |
NHibernate | HQL | Post | 207.84 us | 6.0000 | 0.7500 | - | 35 KB |
EF Core | 'First (No Tracking)' | Post | 213.58 us | 4.2500 | 0.7500 | 0.2500 | 21.36 KB |
EF Core | SqlQuery | Post | 247.25 us | 6.5000 | - | - | 20.56 KB |
EF 6 | First | Post | 247.53 us | 15.5000 | - | - | 48.29 KB |
NHibernate | Criteria | Post | 253.30 us | 13.2500 | 1.2500 | 0.2500 | 65.32 KB |
EF 6 | 'First (No Tracking)' | Post | 265.80 us | 10.5000 | 1.0000 | - | 55.09 KB |
LINQ to SQL | ExecuteQuery | Post | 284.74 us | 7.0000 | 1.0000 | 0.5000 | 42.33 KB |
NHibernate | SQL | Post | 313.85 us | 26.5000 | 1.0000 | - | 101.01 KB |
LINQ to SQL | First | Post | 968.14 us | 4.0000 | 1.0000 | - | 14.68 KB |
NHibernate | LINQ | Post | 1,062.16 us | 11.0000 | 2.0000 | - | 62.37 KB |
Feel free to submit patches that include other ORMs - when running benchmarks, be sure to compile in Release and not attach a debugger (Ctrl+F5).
Alternatively, you might prefer Frans Bouma's RawDataAccessBencher test suite or OrmBenchmark.
Parameterized queries
Parameters are passed in as anonymous classes. This allow you to name your parameters easily and gives you the ability to simply cut-and-paste SQL snippets and run them in your db platform's Query analyzer.
new {A = 1, B = "b"} // A will be mapped to the param @A, B to the param @B
List Support
Dapper allows you to pass in IEnumerable<int>
and will automatically parameterize your query.
For example:
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[] { 1, 2, 3 } });
Will be translated to:
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
Literal replacements
Dapper supports literal replacements for bool and numeric types.
connection.Query("select * from User where UserTypeId = {=Admin}", new { UserTypeId.Admin }));
The literal replacement is not sent as a parameter; this allows better plans and filtered index usage but should usually be used sparingly and after testing. This feature is particularly useful when the value being injected is actually a fixed value (for example, a fixed "category id", "status code" or "region" that is specific to the query). For live data where you are considering literals, you might also want to consider and test provider-specific query hints like OPTIMIZE FOR UNKNOWN
with regular parameters.
Buffered vs Unbuffered readers
Dapper's default behavior is to execute your SQL and buffer the entire reader on return. This is ideal in most cases as it minimizes shared locks in the db and cuts down on db network time.
However when executing huge queries you may need to minimize memory footprint and only load objects as needed. To do so pass, buffered: false
into the Query
method.
Multi Mapping
Dapper allows you to map a single row to multiple objects. This is a key feature if you want to avoid extraneous querying and eager load associations.
Example:
Consider 2 classes: 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; }
}
Now let us say that we want to map a query that joins both the posts and the users table. Until now if we needed to combine the result of 2 queries, we'd need a new object to express it but it makes more sense in this case to put the User
object inside the Post
object.
This is the use case for multi mapping. You tell dapper that the query returns a Post
and a User
object and then give it a function describing what you want to do with each of the rows containing both a Post
and a User
object. In our case, we want to take the user object and put it inside the post object. So we write the function:
(post, user) => { post.Owner = user; return post; }
The 3 type arguments to the Query
method specify what objects dapper should use to deserialize the row and what is going to be returned. We're going to interpret both rows as a combination of Post
and User
and we're returning back a Post
object. Hence the type declaration becomes
<Post, User, Post>
Everything put together, looks like this:
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(1, post.Id);
Assert.Equal("Sam", post.Owner.Name);
Assert.Equal(99, post.Owner.Id);
Dapper is able to split the returned row by making an assumption that your Id columns are named Id
or id
. If your primary key is different or you would like to split the row at a point other than Id
, use the optional splitOn
parameter.
Multiple Results
Dapper allows you to process multiple result grids in a single query.
Example:
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();
...
}
Stored Procedures
Dapper fully supports stored procs:
var user = cnn.Query<User>("spGetUser", new {Id = 1},
commandType: CommandType.StoredProcedure).SingleOrDefault();
If you want something more fancy, you can do:
var p = new DynamicParameters();
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); int b = p.Get<int>("@b");
int c = p.Get<int>("@c");
Ansi Strings and varchar
Dapper supports varchar params, if you are executing a where clause on a varchar column using a param be sure to pass it in this way:
Query<Thing>("select * from Thing where Name = @Name", new {Name = new DbString { Value = "abcde", IsFixedLength = true, Length = 10, IsAnsi = true });
On SQL Server it is crucial to use the unicode when querying unicode and ANSI when querying non unicode.
Type Switching Per Row
Usually you'll want to treat all rows from a given table as the same data type. However, there are some circumstances where it's useful to be able to parse different rows as different data types. This is where IDataReader.GetRowParser
comes in handy.
Imagine you have a database table named "Shapes" with the columns: Id
, Type
, and Data
, and you want to parse its rows into Circle
, Square
, or Triangle
objects based on the value of the Type column.
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);
}
}
User Defined Variables in MySQL
In order to use Non-parameter SQL variables with MySql Connector, you have to add the following option to your connection string:
Allow User Variables=True
Make sure you don't provide Dapper with a property to map.
Limitations and caveats
Dapper caches information about every query it runs, this allows it to materialize objects quickly and process parameters quickly. The current implementation caches this information in a ConcurrentDictionary
object. Statements that are only used once are routinely flushed from this cache. Still, if you are generating SQL strings on the fly without using parameters it is possible you may hit memory issues.
Dapper's simplicity means that many feature that ORMs ship with are stripped out. It worries about the 95% scenario, and gives you the tools you need most of the time. It doesn't attempt to solve every problem.
Will Dapper work with my DB provider?
Dapper has no DB specific implementation details, it works across all .NET ADO providers including SQLite, SQL CE, Firebird, Oracle, MySQL, PostgreSQL and SQL Server.
Do you have a comprehensive list of examples?
Dapper has a comprehensive test suite in the test project.
Who is using this?
Dapper is in production use at Stack Overflow.
Dapper - a simple object mapper for .Net的更多相关文章
- 简单对象访问协议(Simple Object Access Protocol),PHP调用SOAP过程中的种种问题;php的soap无故出错的真凶:wsdl缓存
webservice的一种常用实现方式就是soap了.我们后端的JAVA也是用soap的原理实现的.那么我显然首先要上网上搜搜关于soap的文章.最早进入实现的是PHP写的nusoap类.这个n ...
- 轻型ORM--Dapper
分享一个轻型ORM--Dapper选用理由 推荐理由:Dapper只有一个代码文件,完全开源,你可以放在项目里的任何位置,来实现数据到对象的ORM操作,体积小速度快:) Google Code下载地址 ...
- Dapper.net ORM
参考链接:https://github.com/StackExchange/dapper-dot-net Dapper - a simple object mapper for .Net Dapper ...
- Dapper - 一款轻量级对象关系映射(ORM)组件,DotNet 下
Dapper - a simple object mapper for .Net Official Github clone: https://github.com/SamSaffron/dapper ...
- DotNet 资源大全中文版(Awesome最新版)
Awesome系列的.Net资源整理.awesome-dotnet是由quozd发起和维护.内容包括:编译器.压缩.应用框架.应用模板.加密.数据库.反编译.IDE.日志.风格指南等. 算法与数据结构 ...
- Awesome-Link——我的积累、推荐和分享
积累一些自己看过的比较好的技术博客.以后忘记了可以回过头来看,毕竟有些博客已经写的非常的好了.有些自己写的也会列举在上面.同时还包含一些好用的插件.工具.网站等等. github准备长期更新,欢迎st ...
- 【资源大全】.NET资源大全中文版(Awesome最新版)
算法与数据结构(Algorithms and Data structures) 应用程序接口(API) 应用程序框架(Application Frameworks) 模板引擎(Application ...
- 【整理总结】目录 - 代码沉淀 - 常见Nuget包介绍及使用
为了团队项目或者后期代码维护方便,现在项目已经极致推荐使用Nuget管理第三方类库了,所以下面列举的类库,都会在Nuget上获取到.目录排序不分先后,如果查找困难,请使用 ctrl + F 重要提示: ...
- [最新].NET Core ORM 开源项目一览,持续更新
截至2019-05-08共收集27个 .NET Core ORM 开源项目,38个 .NET ORM 开源项目. .NET Core ORM 开源项目收集地址:https://github.com/o ...
随机推荐
- Java学习day4 程序流程控制一
一.分支结构 条件语句:if...else if语句: 一个 if 语句包含一个布尔表达式和一条或多条语句,如果布尔表达式的值为 true,则执行 if 语句中的代码块,否则执行 if 语句块后面的代 ...
- [2019杭电多校第二场][hdu6602]Longest Subarray(线段树)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6602 题目大意为求最长的区间,满足C种数字在区间内要么不出现,要么出现的次数都不小于K. 大致的分析一 ...
- [luogu4768] [NOI2018] 归程 (Dijkstra+Kruskal重构树)
[luogu4768] [NOI2018] 归程 (Dijkstra+Kruskal重构树) 题面 题面较长,这里就不贴了 分析 看到不能经过有积水的边,即不能经过边权小于一定值的边,我们想到了kru ...
- 洛谷 - P4567 - 文本编辑器 - 无旋Treap
https://www.luogu.org/problem/P4567 事实证明无旋Treap是不是不可能会比Splay快? #include<bits/stdc++.h> using n ...
- maven pom文件元素说明
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/20 ...
- 两种方法删除ArrayList里反复元素
方法一: /** List order not maintained **/ public static void removeDuplicate(ArrayList arlList) { HashS ...
- JS中类或对象的定义说明
本篇文章主要是对JS中类或对象的定义进行说明介绍.我们知道,JS是面向对象的.谈到面向对象,就不可避免的要涉及类的概念.一般像c#,java这些强类型语言都有固定的定义类的语法.而JS的不同之处在于它 ...
- k3 cloud中获取年月日
日期类型字段元素.Date.Year(获取年) 日期类型字段元素.Date.Month(获取月)日期类型字段元素.Date.Day(获取天)
- [小试牛刀]部署在IDEA的JFinal 3.0 demo
进入JFinal 极速开发市区:http://www.jfinal.com/ 如上图,点击右边的最新下载:JFinal 3.0 demo - 此过程跳过注册\登录过程, 进入到如下,下载 下载并解压到 ...
- 关于conda-新手必读
一.管理conda 通过anaconda来安装python及python包,让你不必关心系统是否安装了一些依赖,如zlib等等,anaconda已经集成了这些依赖,可以方便的安装python 下载请点 ...