位於https://github.com/dapperlib/dapper/releases
myget預釋放提要:https://www.myget.org/gallery/dapper
包裹 | nuget穩定 | Nuget預發行 | 下載 | 邁特 |
---|---|---|---|---|
Dapper | ||||
dapper.entityframework | ||||
dapper.entityframework.strongname | ||||
dapper.rainbow | ||||
dapper.sqlbuilder | ||||
dapper.strongname |
包裝目的:
Dapper最初是為堆棧溢出而開發的,但是f/oss。歡迎和邀請贊助 - 請參閱頁面頂部的讚助商鏈接。非常感謝所有贊助Dapper的人(個人或組織),但特別感謝:
Dapper是一個Nuget庫,您可以將其添加到項目中,可以通過DbConnection
實例上的擴展方法來增強您的ado.net連接。這為調用SQL提供了簡單有效的API,並支持同步和異步數據訪問,並允許緩沖和非緩衝查詢。
它提供了多個助手,但是關鍵的API是:
// insert/update/delete etc
var count = connection . Execute ( sql [ , args ] ) ;
// multi-row query
IEnumerable < T > rows = connection . Query < T > ( sql [ , args ] ) ;
// single-row query ({Single|First}[OrDefault])
T row = connection . QuerySingle < T > ( sql [ , args ] ) ;
args
可以在哪裡(除其他事項):
Dictionary<string,object>
DynamicParameters
實例 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 ) ;
此方法將執行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 ) ;
示例用法:
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"
當您已經有現有集合時,另一個示例用法:
var foos = new List < Foo >
{
{ new Foo { A = 1 , B = 1 } }
{ new Foo { A = 2 , B = 2 } }
{ new Foo { A = 3 , B = 3 } }
} ;
var count = connection . Execute ( @"insert MyTable(colA, colB) values (@a, @b)" , foos ) ;
Assert . Equal ( foos . Count , count ) ;
這適用於某些T的任何IEnumerable<T>
。
Dapper的一個關鍵功能是性能。以下指標表明,針對DB執行SELECT
語句需要多長時間(以各種配置,每個標記為標記),並將返回的數據映射到對象。
基準可以在dapper.tests.smormance中找到(歡迎貢獻!),可以通過:
dotnet run --project . b enchmarks D apper.Tests.Performance -c Release -f net8.0 -- -f * --join
最新運行的輸出是:
BenchmarkDotNet v0.13.7, Windows 10 (10.0.19045.3693/22H2/2022Update)
Intel Core i7-3630QM CPU 2.40GHz (Ivy Bridge), 1 CPU, 8 logical and 4 physical cores
.NET SDK 8.0.100
[Host] : .NET 8.0.0 (8.0.23.53103), X64 RyuJIT AVX
ShortRun : .NET 8.0.0 (8.0.23.53103), X64 RyuJIT AVX
ORM | 方法 | 返回 | 意思是 | stddev | 錯誤 | Gen0 | Gen1 | Gen2 | 分配 |
---|---|---|---|---|---|---|---|---|---|
Dapper Cache撞擊 | 執行eparameters_cache | 空白 | 96.75美國 | 0.668美國 | 1.010美國 | 0.6250 | - | - | 2184 b |
Dapper Cache撞擊 | QUERYFIRSTPARAMETERS_CACHE | 空白 | 96.86美國 | 0.493美國 | 0.746美國 | 0.8750 | - | - | 2824 b |
手工編碼 | SQLCommand | 郵政 | 119.70美國 | 0.706美國 | 1.067美國 | 1.3750 | 1.0000 | 0.1250 | 7584 b |
手工編碼 | 數據表 | 動態的 | 126.64美國 | 1.239美國 | 1.873美國 | 3.0000 | - | - | 9576 b |
sqlmarshal | SQLCommand | 郵政 | 132.36美國 | 1.008美國 | 1.523美國 | 2.0000 | 1.0000 | 0.2500 | 11529 b |
Dapper | QUERYFIRSTORDEFAULT | 郵政 | 133.73美國 | 1.301美國 | 2.186美國 | 1.7500 | 1.5000 | - | 11608 b |
強大 | 詢問 | 動態的 | 133.92我們 | 1.075美國 | 1.806美國 | 2.0000 | 1.7500 | - | 12710 b |
linq到DB | 詢問 | 郵政 | 134.24美國 | 1.068美國 | 1.614美國 | 1.7500 | 1.2500 | - | 10904 b |
repodb | ExecuteQuery | 郵政 | 135.83美國 | 1.839美國 | 3.091我們 | 1.7500 | 1.5000 | - | 11649 b |
Dapper | “查詢(緩衝)” | 郵政 | 136.14美國 | 1.755美國 | 2.653美國 | 2.0000 | 1.5000 | - | 11888 b |
強大 | 詢問 | 郵政 | 137.96美國 | 1.485美國 | 2.244美國 | 2.2500 | 1.2500 | - | 12201 b |
Dapper | QUERYFIRSTORDEFAULT | 動態的 | 139.04美國 | 1.507我們 | 2.279美國 | 3.5000 | - | - | 11648 b |
強大 | 單fromquery | 動態的 | 139.74美國 | 2.521我們 | 3.811我們 | 2.0000 | 1.7500 | - | 12710 b |
Dapper | “查詢(緩衝)” | 動態的 | 140.13美國 | 1.382我們 | 2.090美國 | 2.0000 | 1.5000 | - | 11968 b |
ServiceStack | 單側 | 郵政 | 140.76美國 | 1.147美國 | 2.192我們 | 2.5000 | 1.2500 | 0.2500 | 15248 b |
Dapper | “貢獻” | 郵政 | 141.09美國 | 1.394美國 | 2.108美國 | 2.0000 | 1.5000 | - | 12440 b |
強大 | 單fromquery | 郵政 | 141.17美國 | 1.941美國 | 2.935美國 | 1.7500 | 1.5000 | - | 12201 b |
大量的 | “查詢(動態)” | 動態的 | 142.01美國 | 4.957我們 | 7.494美國 | 2.0000 | 1.5000 | - | 12342 b |
linq到DB | “第一(編譯)” | 郵政 | 144.59美國 | 1.295美國 | 1.958美國 | 1.7500 | 1.5000 | - | 12128 b |
repodb | Queryfield | 郵政 | 148.31美國 | 1.742我們 | 2.633美國 | 2.0000 | 1.5000 | 0.5000 | 13938 b |
規範 | '閱讀<>(元組)' | valuetuple`8 | 148.58美國 | 2.172我們 | 3.283美國 | 2.0000 | 1.7500 | - | 12745 b |
規範 | 'read <()>(命名元組)' | valuetuple`8 | 150.60美國 | 0.658美國 | 1.106我們 | 2.2500 | 2.0000 | 1.2500 | 14562 b |
repodb | 詢問 | 郵政 | 152.34美國 | 2.164美國 | 3.271我們 | 2.2500 | 1.5000 | 0.2500 | 14106 b |
repodb | Querydynamic | 郵政 | 154.15美國 | 4.108我們 | 6.210我們 | 2.2500 | 1.7500 | 0.5000 | 13930 b |
repodb | 查詢某個地方 | 郵政 | 155.90美國 | 1.953美國 | 3.282我們 | 2.5000 | 0.5000 | - | 14858 b |
Dapper Cache撞擊 | 執行原核心_nocache | 空白 | 162.35美國 | 1.584美國 | 2.394美國 | - | - | - | 760 b |
Dapper Cache撞擊 | 執行原始核心_Cache | 空白 | 162.42我們 | 2.740美國 | 4.142我們 | - | - | - | 760 b |
Dapper Cache撞擊 | QUERYFIRSTNOPARAMETERS_CACHE | 空白 | 164.35美國 | 1.206美國 | 1.824美國 | 0.2500 | - | - | 1520 b |
devexpress.xpo | FindObject | 郵政 | 165.87美國 | 1.012我們 | 1.934美國 | 8.5000 | - | - | 28099 b |
Dapper Cache撞擊 | QUERYFIRSTNOPARAMETERS_NOCACHE | 空白 | 173.87美國 | 1.178美國 | 1.781美國 | 0.5000 | - | - | 1576 b |
linq到DB | 第一的 | 郵政 | 175.21我們 | 2.292我們 | 3.851美國 | 2.0000 | 0.5000 | - | 14041 b |
EF 6 | sqlquery | 郵政 | 175.36美國 | 2.259美國 | 3.415美國 | 4.0000 | 0.7500 | - | 24209 b |
規範 | '讀<>(class)' | 郵政 | 186.37美國 | 1.305美國 | 2.496我們 | 3.0000 | 0.5000 | - | 17579 b |
devexpress.xpo | getObjectByKey | 郵政 | 186.78美國 | 3.407我們 | 5.151我們 | 4.5000 | 1.0000 | - | 30114 b |
Dapper | “查詢(未掩蓋)” | 動態的 | 194.62美國 | 1.335美國 | 2.019我們 | 1.7500 | 1.5000 | - | 12048 b |
Dapper | “查詢(未掩蓋)” | 郵政 | 195.01美國 | 0.888美國 | 1.343美國 | 2.0000 | 1.5000 | - | 12008 b |
devexpress.xpo | 詢問 | 郵政 | 199.46美國 | 5.500美國 | 9.243美國 | 10.0000 | - | - | 32083 b |
貝爾格萊德 | firstordEfault | 任務`1 | 228.70美國 | 2.181我們 | 3.665美國 | 4.5000 | 0.5000 | - | 20555 b |
EF核心 | “第一(編譯)” | 郵政 | 265.45美國 | 17.745美國 | 26.828美國 | 2.0000 | - | - | 7521 b |
NHIBERNATE | 得到 | 郵政 | 276.02美國 | 8.029我們 | 12.139美國 | 6.5000 | 1.0000 | - | 29885 b |
NHIBERNATE | HQL | 郵政 | 277.74美國 | 13.032美國 | 19.703美國 | 8.0000 | 1.0000 | - | 31886 b |
NHIBERNATE | 標準 | 郵政 | 300.22美國 | 14.908美國 | 28.504美國 | 13.0000 | 1.0000 | - | 57562 b |
EF 6 | 第一的 | 郵政 | 310.55美國 | 27.254美國 | 45.799美國 | 13.0000 | - | - | 43309 b |
EF核心 | 第一的 | 郵政 | 317.12美國 | 1.354美國 | 2.046我們 | 3.5000 | - | - | 11306 b |
EF核心 | sqlquery | 郵政 | 322.34美國 | 23.990美國 | 40.314美國 | 5.0000 | - | - | 18195 b |
NHIBERNATE | SQL | 郵政 | 325.54美國 | 3.937我們 | 7.527美國 | 22.0000 | 1.0000 | - | 80007 b |
EF 6 | “第一個(無跟踪)” | 郵政 | 331.14美國 | 27.760美國 | 46.649美國 | 12.0000 | 1.0000 | - | 50237 b |
EF核心 | “第一個(無跟踪)” | 郵政 | 337.82美國 | 27.814美國 | 46.740美國 | 3.0000 | 1.0000 | - | 17986 b |
NHIBERNATE | linq | 郵政 | 604.74美國 | 5.549我們 | 10.610美國 | 10.0000 | - | - | 46061 b |
Dapper Cache撞擊 | 執行eparameters_nocache | 空白 | 623.42美國 | 3.978美國 | 6.684美國 | 3.0000 | 2.0000 | - | 10001 b |
Dapper Cache撞擊 | QUERYFIRSTPARAMETERS_NOCACHE | 空白 | 630.77美國 | 3.027我們 | 4.576我們 | 3.0000 | 2.0000 | - | 10640 b |
請隨時提交包含其他ORM的補丁 - 運行基準測試時,請確保在發行版中進行編譯,而不要附加調試器( CTRL + F5 )。
另外,您可能更喜歡Frans Bouma的RawDataAccessbencher測試套件或Ormbenchmark。
參數通常作為匿名類傳遞。這使您可以輕鬆地命名參數,並使您能夠簡單地剪切和剪切SQL摘要並將其運行到DB平台的查詢分析儀中。
new { A = 1 , B = " b " } // A will be mapped to the param @A, B to the param @B
參數也可以使用DynamicParameters類動態構建。這允許構建動態SQL語句,同時仍使用參數以進行安全性和性能。
var sqlPredicates = new List < string > ( ) ;
var queryParams = new DynamicParameters ( ) ;
if ( boolExpression )
{
sqlPredicates . Add ( " column1 = @param1 " ) ;
queryParams . Add ( " param1 " , dynamicValue1 , System . Data . DbType . Guid ) ;
} else {
sqlPredicates . Add ( " column2 = @param2 " ) ;
queryParams . Add ( " param2 " , dynamicValue2 , System . Data . DbType . String ) ;
}
DynamicParameters還支持從不同類型的現有對象複製多個參數。
var queryParams = new DynamicParameters ( objectOfType1 ) ;
queryParams . AddDynamicParams ( objectOfType2 ) ;
當實現傳遞到Execute
或Query
函數的IDynamicParameters
接口的對象時,將通過此接口提取參數值。顯然,最有可能用於此目的的對像類是內置的DynamicParameters
類。
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 UserTypeId = {=Admin} " , new { UserTypeId . Admin } ) ;
文字替換不是作為參數發送的;這允許更好的計劃和過濾索引使用情況,但通常應在測試後謹慎使用。當注入的值實際上是一個固定值時(例如,固定的“類別ID”,“狀態代碼”或“區域”特定於查詢)時,此功能特別有用。對於您正在考慮文字的實時數據,您可能還需要考慮和測試特定於特定於提供商的查詢提示,例如使用常規參數OPTIMIZE FOR UNKNOWN
。
Dapper的默認行為是在返回時執行您的SQL並緩衝整個讀者。在大多數情況下,這是理想的選擇,因為它可以最大程度地減少數據庫中的共享鎖,並在DB網絡時間縮短。
但是,執行大量查詢時,您可能需要最大程度地減少內存足跡,而僅根據需要加載對象。為此, buffered: false
到Query
方法中。
Dapper允許您將一行映射到多個對象。如果您想避免其他查詢和急切的負載關聯,這是一個關鍵功能。
例子:
考慮2類: Post
和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 ; }
}
現在,我們要說的是,我們要映射一個與帖子和用戶表相連的查詢。到目前為止,如果我們需要組合2個查詢的結果,我們需要一個新的對象來表達它,但是在這種情況下,將User
對象放在Post
對像中更有意義。
這是用於多映射的用例。您告訴Dapper,查詢返回Post
和User
對象,然後給它一個函數,描述您要對包含Post
和User
對象的每個行要做什麼。在我們的情況下,我們希望將用戶對象放在郵政對像中。因此,我們編寫功能:
( 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完全支持存儲的Procs:
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 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和ANSI時使用Unicode至關重要。
通常,您需要將來自給定表的所有行視為相同的數據類型。但是,在某些情況下,能夠將不同的行分解為不同的數據類型很有用。這就是IDataReader.GetRowParser
派上用場的地方。
想像一下,您的數據庫表名為“形狀”,帶有列: Id
, Type
和Data
,並且要根據類型列的值將其行分解為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 ) ;
}
}
為了將非參數SQL變量與MySQL連接器一起使用,您必須在連接字符串中添加以下選項:
Allow User Variables=True
確保您不會為Dapper提供用於映射的屬性。
Dapper緩存了有關其運行的每個查詢的信息,這使其可以快速實現對象并快速處理參數。當前的實現將此信息緩存在ConcurrentDictionary
像中。通常將一次使用的語句從此緩存中通常沖洗掉。不過,如果您在不使用參數的情況下即時生成SQL字符串,則可能會遇到內存問題。
Dapper的簡單性意味著Orms發貨的許多功能都被剝離了。它擔心95%的情況,並為您提供大部分時間所需的工具。它不會試圖解決所有問題。
DAPPER沒有DB特定的實現詳細信息,它在所有.NET ADO提供商中都起作用,包括SQLITE,SQL CE,Firebird,Oracle,MariaDB,MySQL,MySQL,PostgreSQL和SQL Server。
Dapper在測試項目中具有全面的測試套件。
Dapper在堆棧溢出處的生產使用。