Dapper - a simple object mapper for .Net
Dapper是.NET下一個micro ORM,它和Entity Framework或Nhibnate不同,屬於輕量級的,並且是半自動的,也就是說實體類都要自己寫。
下載地址:https://github.com/StackExchange/Dapper/
優點
1. 使用Dapper可以自動進行對象映射。
2. 輕量級,單文件。
3. 支持多數據庫。
4. Dapper原理通過Emit反射IDataReader的序列隊列,來快速的得到和產生對象。
5. 可以映射一對一,一對多,多對多等多種關系。
發布說明
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是一個NuGet庫,您可以將其添加到您的項目中,以擴展IDbConnection接口。
連接語句
var _connectionString = ConfigurationManager.ConnectionStrings["SqlServer"].ConnectionString; IDbConnection conn = new SqlConnection(_connectionString);
使用dapper不需要考慮conn是否連接,在執行dapper時自行判斷 open狀態,如果沒有打開它會自己打開。一條查詢的時候不用管,多個查詢在一起的時候,建議自己打開,寫事務的時候還是需要自己打開的哦。
它提供了3個 helpers:
1、執行查詢並將結果映射到強類型列表
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 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);
2、執行查詢並將其映射到動態對象列表
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").AsList();
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);
3、執行不返回任何結果的命令
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 rows inserted: "1,1", "2,2" and "3,3"
這適用於任何為某個T實現IEnumerable的參數。
query = "INSERT INTO Dogs(Id,Name,Age,Weight) VALUES(@Id,@Name,@Age,@Weight)"; conn.Execute(query, new List<Dog>(){ new Dog{ Id = DateTime.Now.Ticks+1, Name = "小紅1", Age = 2, Weight = 10 }, new Dog { Id = DateTime.Now.Ticks+2, Name = "小紅2", Age = 2, Weight = 10 }, new Dog { Id = DateTime.Now.Ticks+3, Name = "小紅3", Age = 2, Weight = 10 }, new Dog { Id = DateTime.Now.Ticks+4, Name = "小紅3", Age = 2, Weight = 10 }, });
參數化查詢
參數作為匿名類傳入。這允許您輕松地命名參數,並使您能夠簡單地剪切和粘貼SQL片段,並在db平台的查詢分析器中運行它們。
new {A = 1, B = "b"} // A will be mapped to the param @A, B to the param @B
List 支持
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中in,like的使用
list = conn.Query<Dog>("SELECT * FROM Dogs WHERE id IN @ids ", new { ids = new long[] { list[0].Id, list[1].Id, list[2].Id } }).ToList(); list = conn.Query<Dog>("SELECT * FROM Dogs WHERE name LIKE @name ", new { name = $"%{name}%" }).ToList();
文字字面替換
Dapper支持對bool和numeric類型進行文字替換。
connection.Query("select * from User where UserTypeId = {=Admin}", new { UserTypeId.Admin });
文字替換不作為參數發送;這允許更好的計划和過濾索引的使用,但通常應該在測試后謹慎使用。
當注入的值實際上是固定值時(例如,特定於查詢的固定“category id”、“status code”或“region”),此特性尤其有用。
對於正在考慮文本的實時數據,您可能還需要考慮和測試provider-specific的查詢提示,比如使用常規參數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表的查詢。
直到現在,如果我們需要合並兩個查詢的結果,我們需要一個新的對象來表示它,但是在這種情況下,將User對象放在Post對象中更有意義。
這是多映射的用例。您告訴dapper查詢返回一個Post和一個User對象,然后給它一個函數,描述您想對包含Post和User對象的每一行做什么。在本例中,我們希望獲取user對象並將其放入post對象中。我們寫下函數:
(post, user) => { post.Owner = user; return post; }
查詢方法的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");
Ansi字符串和varchar
Dapper支持varchar params,如果你使用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,和在查詢非unicode時使用ANSI是至關重要的。
每行類型切換
通常,您希望將給定表中的所有行視為相同的數據類型。
但是,在某些情況下,能夠將不同的行解析為不同的數據類型是很有用的。這就是我成為玩家的地方。IDataReader.GetRowParser
遲早會派上用場。
假設您有一個名為“Shapes”的數據庫表,列有:Id、Type和Data,並且您希望根據Type列的值將其行解析為Circle
, Square
, or Triangle
對象。
var shapes = new List<IShape>(); using (var reader = connection.ExecuteReader("select * from Shapes")) { // 您期望的每種類型生成一個行解析器。
// 通用類型<IShape>是解析器將返回的內容。
// 參數(typeof(*))是要解析的具體類型。
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); } }
在MySQL用戶定義的變量
為了使用非參數SQL變量與MySql連接器,你必須添加以下選項到你的連接字符串:
Allow User Variables=True
請確保沒有向Dapper提供要映射的屬性。
限制和警告
Dapper緩存關於它運行的每個查詢的信息,這允許它快速物化對象並快速處理參數。
當前實現將此信息緩存到一個ConcurrentDictionary對象中。只使用一次的語句通常會從該緩存中刷新。但是,如果您在不使用參數的情況下動態地生成SQL字符串,那么可能會遇到內存問題。
Dapper的簡潔性意味着ORMs自帶的許多功能都被去掉了。它關注95%的場景,並為您提供大多數情況下需要的工具。它並不試圖解決所有的問題。
Dapper會與我的DB provider一起工作嗎?
Dapper沒有DB特定的實現細節,它可以跨所有。net ADO提供程序工作,包括SQLite, SQL CE, Firebird, Oracle, MySQL, PostgreSQL and SQL Server.
你有一個完整的例子列表嗎??
Dapper有一個全面的測試套件:test project.
誰在用這個?
Dapper在生產中使用:Stack Overflow.
擴展:Dapper To Linq框架
此框架是Dapper的擴展,效率優於EntityFramwork,並且支持.NetFramework和.NetCore框架
https://www.cnblogs.com/kogel/p/10805696.html
EntityFrameworkCore結合Dapper的使用
基本使用:
我們可以通過DbContext.Database.GetDbConnection() 獲得 EF 使用的數據庫連接對象 DbConnection,這允許我們使用 Dapper 。
與DbContext不同的是,使用 DbConnection 進行的 【寫】 操作將直接在數據庫生效,不需要調用 DbContext.SaveChanges()。
使用事務:
我們可以通過 DbContext.Database.BeginTransaction() 獲取到 IDbContextTransaction 對象,但它不是 IDbTransaction,所以沒法直接在 Dapper 中使用。
幸運的是微軟提供了擴展方法 IDbContextTransaction.GetDbTransaction() 獲取 IDbTransaction ,為了統一控制 EFCore 和 Dapper 的事務,所以在 Dapper 執行數據庫操作時,必須將 IDbTransaction 傳到 transaction 參數中。