本博客做者與Github上做者(cnxy)實爲同一個做者。因爲筆者翻譯水平有限,文本中錯誤不免,歡迎指正!git
請見 stackexchange.github.io/Dappergithub
Nuget穩定版:web
https://www.nuget.org/packages/Dapper
Visual Studio 程序包管理器控制檯:sql
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)
示例:app
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);
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(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);
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=1, b=2 }); Assert.Equal(2, count);
還容許使用相同的參數簽名方便有效地屢次執行命令(例如批量加載數據)
示例:
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行: "1,1", "2,2" 與 "3,3"
這適用於已經實現IEnumerable接口的集合對象T。
Dapper的一個關鍵特性是性能。 如下度量標準顯示了對DB執行500個SELECT
語句並將返回的數據映射到對象所需的時間。
性能測試分爲3個列表:
方法 | 執行時間 | 備註 |
---|---|---|
手工編碼 (使用 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 |
方法 | 執行時間 | 備註 |
---|---|---|
Dapper ExecuteMapperQuery (動態) |
48ms | |
Massive | 52ms | |
Simple.Data | 95ms |
方法 | 執行時間 | 備註 |
---|---|---|
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 = 1, 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[] { 1, 2, 3 } });
以上將被轉換成:
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 = 1}));
文字替換不做爲參數發送; 更好的計劃和過濾索引用法將被容許,但一般應謹慎在測試後使用。 當注入的值其實是固定值(例如,特定於查詢的「類別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(1, post.Id); Assert.Equal("Sam", post.Owner.Name); Assert.Equal(99, 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 = 1}, commandType: CommandType.StoredProcedure).SingleOrDefault();
若是你想要更有趣的東西,你能夠這樣作:
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");
Dapper支持varchar參數,若是使用param在varchar列上執行where子句,請確保以這種方式傳遞它:
Query<Thing>("select * from Thing where Name = @Name", new {Name = new DbString { Value = "abcde", IsFixedLength = true, Length = 10, 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沒有特定於DB的實現細節,它適用於全部.NET ADO提供程序,包括SQLite(https://www.sqlite.org/),SQL CE,Firebird,Oracle,MySQL,PostgreSQL和SQL Server。
Dapper有一個完整位於測試工程的測試套件。
Stack Overflow正在使用Dapper。