Localizado em https://github.com/dapperlib/dapper/releases
MyGet Feed de pré-lançamento: https://www.myget.org/gallery/dapper
Pacote | Nuget estável | NUGET Pré-lançamento | Downloads | Myget |
---|---|---|---|---|
Dapper | ||||
Dapper.EntityFramework | ||||
Dapper.EntityFramework.strongname | ||||
Dapper.Rainbow | ||||
Dapper.SQLBuilder | ||||
Dapper.strongname |
Fins de embalagem:
Dapper foi desenvolvido originalmente para e por excesso de pilha, mas é f/OSS. O patrocínio é bem -vindo e convidado - consulte o link do patrocinador na parte superior da página. Um enorme agradecimento a todos (indivíduos ou organizações) que patrocinaram Dapper, mas um enorme agradecimento, em particular a:
O Dapper é uma biblioteca Nuget que você pode adicionar ao seu projeto que aprimorará suas conexões ADO.NET através de métodos de extensão na sua instância DbConnection
. Isso fornece uma API simples e eficiente para invocar o SQL, com suporte para acesso de dados síncronos e assíncronos, e permite consultas tamponadas e não buffers.
Ele fornece vários ajudantes, mas as principais APIs são:
// 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 ] ) ;
onde args
podem estar (entre outras coisas):
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 ) ;
Este método executará o SQL e retornará uma lista dinâmica.
Exemplo de uso:
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 ) ;
Exemplo de uso:
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 ) ;
A mesma assinatura também permite executar de forma conveniente e eficiente um comando várias vezes (por exemplo, para dados de carga em massa)
Exemplo de uso:
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"
Outro exemplo de uso quando você já tem uma coleção existente:
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 ) ;
Isso funciona para qualquer parâmetro que implementa IEnumerable<T>
para alguns T.
Um recurso importante do Dapper é o desempenho. As métricas a seguir mostram quanto tempo leva para executar uma instrução SELECT
contra um banco de dados (em várias configurações, cada uma marcada) e mapear os dados retornados aos objetos.
Os benchmarks podem ser encontrados no Dapper.Tests.Performance (contribuições são bem -vindas!) E podem ser executadas via:
dotnet run --project . b enchmarks D apper.Tests.Performance -c Release -f net8.0 -- -f * --join
A saída da última execução é:
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 | Método | Retornar | Significar | Stddev | Erro | Gen0 | Gen1 | Gen2 | Alocado |
---|---|---|---|---|---|---|---|---|---|
Impacto de cache da Dapper | ExecutiveParameters_cache | Vazio | 96,75 nós | 0,668 nós | 1.010 US | 0,6250 | - | - | 2184 b |
Impacto de cache da Dapper | Queryfirstparameters_cache | Vazio | 96,86 nós | 0,493 nós | 0,746 nós | 0,8750 | - | - | 2824 b |
Codificado à mão | SQLCommand | Publicar | 119,70 nós | 0,706 nós | 1.067 nós | 1.3750 | 1.0000 | 0,1250 | 7584 b |
Codificado à mão | DataTable | dinâmico | 126.64 US | 1.239 nós | 1.873 nós | 3.0000 | - | - | 9576 b |
SQLMARSHAL | SQLCommand | Publicar | 132.36 nós | 1.008 nós | 1.523 nós | 2.0000 | 1.0000 | 0,2500 | 11529 b |
Dapper | Queryfirstordefault | Publicar | 133.73 nós | 1.301 US | 2.186 nós | 1.7500 | 1.5000 | - | 11608 b |
Poderoso | Consulta | dinâmico | 133.92 nós | 1.075 nós | 1.806 nós | 2.0000 | 1.7500 | - | 12710 b |
Linq para DB | Consulta | Publicar | 134.24 nós | 1.068 nós | 1.614 nós | 1.7500 | 1.2500 | - | 10904 b |
Repodb | ExecuteQuery | Publicar | 135.83 nós | 1.839 nós | 3.091 US | 1.7500 | 1.5000 | - | 11649 b |
Dapper | 'Consulta (buffer)' | Publicar | 136.14 US | 1.755 US | 2.653 nós | 2.0000 | 1.5000 | - | 11888 b |
Poderoso | Consulta | Publicar | 137.96 nós | 1.485 US | 2.244 nós | 2.2500 | 1.2500 | - | 12201 b |
Dapper | Queryfirstordefault | dinâmico | 139.04 nós | 1.507 nós | 2.279 nós | 3.5000 | - | - | 11648 b |
Poderoso | Único daquela | dinâmico | 139.74 nós | 2.521 US | 3.811 nós | 2.0000 | 1.7500 | - | 12710 b |
Dapper | 'Consulta (buffer)' | dinâmico | 140.13 US | 1.382 US | 2.090 nós | 2.0000 | 1.5000 | - | 11968 b |
Servicestack | SinglebyId | Publicar | 140.76 US | 1.147 nós | 2.192 US | 2.5000 | 1.2500 | 0,2500 | 15248 b |
Dapper | 'Contribe Get' | Publicar | 141.09 EUA | 1.394 nós | 2.108 nós | 2.0000 | 1.5000 | - | 12440 b |
Poderoso | Único daquela | Publicar | 141.17 US | 1.941 US | 2.935 nós | 1.7500 | 1.5000 | - | 12201 b |
Enorme | 'Consulta (dinâmica)' | dinâmico | 142.01 US | 4.957 nós | 7.494 nós | 2.0000 | 1.5000 | - | 12342 b |
Linq para DB | 'Primeiro (compilado)' | Publicar | 144.59 nós | 1.295 US | 1.958 nós | 1.7500 | 1.5000 | - | 12128 b |
Repodb | Queryfield | Publicar | 148.31 US | 1.742 US | 2.633 nós | 2.0000 | 1.5000 | 0,5000 | 13938 b |
Norma | 'Leia <> (tuplas)' | Valuetuple`8 | 148.58 EUA | 2.172 US | 3.283 nós | 2.0000 | 1.7500 | - | 12745 b |
Norma | 'Leia <()> (chamado tuplas)' | Valuetuple`8 | 150,60 nós | 0,658 nós | 1.106 nós | 2.2500 | 2.0000 | 1.2500 | 14562 b |
Repodb | Consulta | Publicar | 152.34 US | 2.164 nós | 3.271 US | 2.2500 | 1.5000 | 0,2500 | 14106 b |
Repodb | Querydynamic | Publicar | 154.15 nós | 4.108 nós | 6.210 US | 2.2500 | 1.7500 | 0,5000 | 13930 b |
Repodb | Querywhere | Publicar | 155.90 nós | 1.953 nós | 3.282 US | 2.5000 | 0,5000 | - | 14858 b |
Impacto de cache da Dapper | ExecutenOparameters_nocache | Vazio | 162.35 nós | 1.584 US | 2.394 nós | - | - | - | 760 b |
Impacto de cache da Dapper | ExecutenOparameters_cache | Vazio | 162.42 nós | 2.740 nós | 4.142 US | - | - | - | 760 b |
Impacto de cache da Dapper | Queryfirstnoparameters_cache | Vazio | 164.35 nós | 1.206 nós | 1.824 US | 0,2500 | - | - | 1520 b |
Devexpress.xpo | FindObject | Publicar | 165,87 nós | 1.012 US | 1.934 nós | 8.5000 | - | - | 28099 b |
Impacto de cache da Dapper | Queryfirstnoparameters_nocache | Vazio | 173.87 nós | 1.178 nós | 1.781 US | 0,5000 | - | - | 1576 b |
Linq para DB | Primeiro | Publicar | 175.21 US | 2.292 US | 3.851 US | 2.0000 | 0,5000 | - | 14041 b |
EF 6 | SQLQUERY | Publicar | 175.36 nós | 2.259 nós | 3.415 US | 4.0000 | 0,7500 | - | 24209 b |
Norma | 'Leia <> (classe)' | Publicar | 186.37 EUA | 1.305 nós | 2.496 nós | 3.0000 | 0,5000 | - | 17579 b |
Devexpress.xpo | GetObjectbyKey | Publicar | 186.78 EUA | 3.407 nós | 5.151 US | 4.5000 | 1.0000 | - | 30114 b |
Dapper | 'Consulta (não buffer)' | dinâmico | 194.62 EUA | 1.335 nós | 2.019 nós | 1.7500 | 1.5000 | - | 12048 b |
Dapper | 'Consulta (não buffer)' | Publicar | 195.01 EUA | 0,888 EUA | 1.343 nós | 2.0000 | 1.5000 | - | 12008 b |
Devexpress.xpo | Consulta | Publicar | 199,46 nós | 5.500 nós | 9.243 nós | 10.0000 | - | - | 32083 b |
Belgrado | FirstorDefault | Tarefa`1 | 228.70 nós | 2.181 US | 3.665 nós | 4.5000 | 0,5000 | - | 20555 b |
EF núcleo | 'Primeiro (compilado)' | Publicar | 265.45 nós | 17.745 nós | 26.828 nós | 2.0000 | - | - | 7521 b |
Nibernate | Pegar | Publicar | 276.02 US | 8.029 nós | 12.139 EUA | 6.5000 | 1.0000 | - | 29885 b |
Nibernate | HQL | Publicar | 277.74 US | 13.032 US | 19.703 nós | 8.0000 | 1.0000 | - | 31886 b |
Nibernate | Critérios | Publicar | 300.22 US | 14.908 nós | 28.504 nós | 13.0000 | 1.0000 | - | 57562 b |
EF 6 | Primeiro | Publicar | 310.55 US | 27.254 US | 45.799 nós | 13.0000 | - | - | 43309 b |
EF núcleo | Primeiro | Publicar | 317.12 US | 1.354 US | 2.046 nós | 3.5000 | - | - | 11306 b |
EF núcleo | SQLQUERY | Publicar | 322.34 US | 23.990 nós | 40.314 US | 5.0000 | - | - | 18195 b |
Nibernate | SQL | Publicar | 325.54 US | 3.937 nós | 7.527 nós | 22.0000 | 1.0000 | - | 80007 b |
EF 6 | 'Primeiro (sem rastreamento)' | Publicar | 331.14 US | 27.760 nós | 46.649 nós | 12.0000 | 1.0000 | - | 50237 b |
EF núcleo | 'Primeiro (sem rastreamento)' | Publicar | 337.82 US | 27.814 EUA | 46.740 nós | 3.0000 | 1.0000 | - | 17986 b |
Nibernate | Linq | Publicar | 604.74 nós | 5.549 nós | 10.610 nós | 10.0000 | - | - | 46061 b |
Impacto de cache da Dapper | ExecutiveParameters_nocache | Vazio | 623.42 US | 3.978 nós | 6.684 US | 3.0000 | 2.0000 | - | 10001 b |
Impacto de cache da Dapper | QueryfirstParameters_nocache | Vazio | 630,77 nós | 3.027 nós | 4.576 nós | 3.0000 | 2.0000 | - | 10640 b |
Sinta -se à vontade para enviar patches que incluam outros ORMs - ao executar benchmarks, não se esqueça de compilar na liberação e não anexar um depurador ( Ctrl + F5 ).
Como alternativa, você pode preferir o conjunto de testes RawDataAccessBencher de Frans Bouma ou Ormbenchmark.
Os parâmetros geralmente são passados como classes anônimas. Isso permite que você nomeie seus parâmetros facilmente e oferece a capacidade de simplesmente cortar e colar trechos de SQL e executá-los no analisador de consulta da plataforma do seu banco de dados.
new { A = 1 , B = " b " } // A will be mapped to the param @A, B to the param @B
Os parâmetros também podem ser construídos dinamicamente usando a classe DynamicParameters. Isso permite a criação de uma instrução SQL dinâmica enquanto ainda usa parâmetros para segurança e desempenho.
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 ) ;
}
O DynamicParameters também suporta copiar vários parâmetros de objetos existentes de diferentes tipos.
var queryParams = new DynamicParameters ( objectOfType1 ) ;
queryParams . AddDynamicParams ( objectOfType2 ) ;
Quando um objeto que implementa a interface IDynamicParameters
passados para funções Execute
ou Query
, os valores dos parâmetros serão extraídos por essa interface. Obviamente, a classe de objeto mais provável a ser usada para esse fim seria a classe DynamicParameters
interna.
O Dapper permite que você passe um IEnumerable<int>
e parametrizará automaticamente sua consulta.
Por exemplo:
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 } } ) ;
Será traduzido para:
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
O Dapper suporta substituições literais para os tipos de bool e numéricos.
connection . Query ( " select * from User where UserTypeId = {=Admin} " , new { UserTypeId . Admin } ) ;
A substituição literal não é enviada como um parâmetro; Isso permite melhores planos e uso de índice filtrado, mas geralmente deve ser usado com moderação e após o teste. Esse recurso é particularmente útil quando o valor injetado é na verdade um valor fixo (por exemplo, um "ID da categoria" fixo, "código de status" ou "região" que é específico para a consulta). Para dados ao vivo em que você está considerando literais, você também pode considerar e testar dicas de consulta específicas do provedor, como OPTIMIZE FOR UNKNOWN
com parâmetros regulares.
O comportamento padrão do Dapper é executar seu SQL e buffer o leitor inteiro no retorno. Isso é ideal na maioria dos casos, pois minimiza os bloqueios compartilhados no banco de dados e reduz o tempo de rede de dB.
No entanto, ao executar consultas enormes, pode ser necessário minimizar a pegada de memória e carregar apenas objetos conforme necessário. Para fazer isso, passe buffered: false
no método Query
.
Dapper permite mapear uma única linha para vários objetos. Este é um recurso essencial se você deseja evitar consultas estranhas e associações de carga ansiosa.
Exemplo:
Considere 2 classes: Post
e 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 ; }
}
Agora, digamos que queremos mapear uma consulta que une as postagens e a tabela de usuários. Até agora, se precisávamos combinar o resultado de 2 consultas, precisaríamos de um novo objeto para expressá -lo, mas faz mais sentido nesse caso colocar o objeto User
dentro do objeto Post
.
Este é o caso de uso para mapeamento multi. Você diz a Dapper que a consulta retorna uma Post
e um objeto User
e, em seguida, dá uma função que descreve o que você deseja fazer com cada uma das linhas que contêm uma Post
e um objeto User
. No nosso caso, queremos pegar o objeto do usuário e colocá -lo dentro do objeto Post. Então, escrevemos a função:
( post , user ) => { post . Owner = user ; return post ; }
Os 3 argumentos do tipo para o método Query
especificam quais objetos ele deve usar para desserializar a linha e o que será devolvido. Vamos interpretar as duas linhas como uma combinação de Post
e User
e retornamos um objeto Post
. Portanto, a declaração do tipo se torna
< Post , User , Post >
Tudo junto, parece assim:
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 é capaz de dividir a linha devolvida, assumindo que suas colunas de ID são nomeadas Id
ou id
. Se sua chave primária for diferente ou você deseja dividir a linha em um ponto que não seja Id
, use o parâmetro splitOn
opcional.
O Dapper permite processar várias grades de resultados em uma única consulta.
Exemplo:
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 suporta totalmente os procs armazenados:
var user = cnn . Query < User > ( " spGetUser " , new { Id = 1 } ,
commandType : CommandType . StoredProcedure ) . SingleOrDefault ( ) ;
Se você quiser algo mais chique, você pode fazer:
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 " ) ;
O Dapper suporta params Varchar, se você estiver executando uma cláusula WHERE em uma coluna Varchar usando um param, não se esqueça de passar dessa maneira:
Query < Thing > ( " select * from Thing where Name = @Name " , new { Name = new DbString { Value = " abcde " , IsFixedLength = true , Length = 10 , IsAnsi = true } } ) ;
No SQL Server, é crucial usar o unicode ao consultar o Unicode e o ANSI ao consultar não unicode.
Normalmente, você deseja tratar todas as linhas de uma determinada tabela como o mesmo tipo de dados. No entanto, existem algumas circunstâncias em que é útil poder analisar diferentes linhas como diferentes tipos de dados. É aqui que IDataReader.GetRowParser
é útil.
Imagine que você tem uma tabela de banco de dados chamada "Shapes" com as colunas: Id
, Type
e Data
, e deseja analisar suas linhas em objetos Circle
, Square
ou Triangle
com base no valor da coluna do tipo.
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 ) ;
}
}
Para usar variáveis SQL não parâmetro com o MySQL Connector, você deve adicionar a seguinte opção à sua string de conexão:
Allow User Variables=True
Certifique -se de não fornecer à Dapper uma propriedade para mapear.
Dapper cache informações sobre todas as consultas executadas, isso permite materializar os objetos rapidamente e processar os parâmetros rapidamente. A implementação atual armazena essas informações em um objeto ConcurrentDictionary
. As declarações usadas apenas uma vez são rotineiramente liberadas desse cache. Ainda assim, se você estiver gerando strings SQL em tempo real sem usar parâmetros, é possível que você possa atingir problemas de memória.
A simplicidade de Dapper significa que muitos recursos com os quais o ORMS são despojados. Ele se preocupa com o cenário de 95% e oferece as ferramentas que você precisa a maior parte do tempo. Não tenta resolver todos os problemas.
O Dapper não possui detalhes de implementação específicos de banco de dados, funciona em todos os provedores de ADO .NET, incluindo SQLite, SQL CE, Firebird, Oracle, MariaDB, MySQL, PostgreSQL e SQL Server.
Dapper tem uma suíte de teste abrangente no projeto de teste.
Dapper está em uso de produção no Stack Overflow.