Расположен по адресу https://github.com/dapperlib/dapper/releases
Myget Prelease Feed: https://www.myget.org/gallery/dapper
Упаковка | NUGET CENTABLE | Nuget Pre-Lease | Загрузки | Myget |
---|---|---|---|---|
Окрашивать | ||||
Dapper.entityFramework | ||||
Dapper.entityframework.strongname | ||||
Dapper.rainbow | ||||
Dapper.sqlbuilder | ||||
Dapper.strongname |
Цель пакета:
Dapper был первоначально разработан для переполнения стека, но F/OSS. Спонсорство приветствуется и приглашена - см. Связь спонсора в верхней части страницы. Огромная благодарность всем (отдельным лицам или организациям), которые спонсировали Dapper, но в частности, огромная благодарность:
Dapper - это библиотека Nuget, которую вы можете добавить в свой проект, который улучшит ваши подключения Ado.net с помощью методов расширения в вашем экземпляре DbConnection
. Это обеспечивает простой и эффективный API для вызова SQL, при поддержке как синхронного, так и асинхронного доступа к данным, и позволяет как буферизованные, так и не буферированные запросы.
Он предоставляет несколько помощников, но ключевые 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 ) ;
Это работает для любого параметра, который реализует IEnumerable<T>
для некоторого T.
Ключевой особенностью Dapper является производительность. Следующие метрики показывают, сколько времени требуется, чтобы выполнить оператор SELECT
против DB (в различных конфигурации, каждый помечен) и отобразить данные, возвращаемые объектам.
Тесты можно найти в dapper.tests.predication (взносы
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
Орм | Метод | Возвращаться | Иметь в виду | Stddev | Ошибка | Gen0 | Gen1 | Gen2 | Выделено |
---|---|---|---|---|---|---|---|---|---|
Dapper Cache воздействие | ExecuteParameters_cache | Пустота | 96,75 США | 0,668 США | 1.010 США | 0,6250 | - | - | 2184 б |
Dapper Cache воздействие | QueryFirstParameters_cache | Пустота | 96.86 США | 0,493 США | 0,746 США | 0,8750 | - | - | 2824 б |
Кодированный вручную | SQLCommand | Почта | 119,70 США | 0,706 США | 1.067 США | 1.3750 | 1.0000 | 0,1250 | 7584 б |
Кодированный вручную | DataTable | динамика | 126.64 США | 1.239 США | 1.873 США | 3.0000 | - | - | 9576 б |
SQLMARSHAL | SQLCommand | Почта | 132.36 США | 1.008 США | 1.523 США | 2.0000 | 1.0000 | 0,2500 | 11529 б |
Окрашивать | QueryFirstordefault | Почта | 133,73 США | 1.301 США | 2.186 США | 1.7500 | 1,5000 | - | 11608 б |
Могущественный | Запрос | динамика | 133,92 США | 1.075 США | 1.806 США | 2.0000 | 1.7500 | - | 12710 б |
LINQ в DB | Запрос | Почта | 134.24 США | 1.068 США | 1.614 США | 1.7500 | 1.2500 | - | 10904 б |
Repodb | Executequery | Почта | 135,83 США | 1.839 США | 3.091 США | 1.7500 | 1,5000 | - | 11649 б |
Окрашивать | 'Query (Buffled)' | Почта | 136.14 США | 1.755 США | 2.653 США | 2.0000 | 1,5000 | - | 11888 б |
Могущественный | Запрос | Почта | 137,96 США | 1.485 США | 2.244 США | 2.2500 | 1.2500 | - | 12201 б |
Окрашивать | QueryFirstordefault | динамика | 139,04 США | 1,507 США | 2.279 США | 3,5000 | - | - | 11648 б |
Могущественный | SingleFromQuery | динамика | 139,74 США | 2.521 США | 3.811 США | 2.0000 | 1.7500 | - | 12710 б |
Окрашивать | 'Query (Buffled)' | динамика | 140.13 США | 1.382 США | 2.090 США | 2.0000 | 1,5000 | - | 11968 б |
ServiceStack | SingleByid | Почта | 140,76 США | 1.147 США | 2.192 США | 2,5000 | 1.2500 | 0,2500 | 15248 б |
Окрашивать | 'Contrib Get' | Почта | 141.09 США | 1.394 США | 2.108 США | 2.0000 | 1,5000 | - | 12440 б |
Могущественный | SingleFromQuery | Почта | 141.17 США | 1.941 США | 2.935 США | 1.7500 | 1,5000 | - | 12201 б |
Массивный | 'Query (Dynamic)' | динамика | 142.01 США | 4.957 США | 7.494 США | 2.0000 | 1,5000 | - | 12342 б |
LINQ в DB | 'Первый (скомпилирован)' | Почта | 144,59 США | 1.295 США | 1.958 США | 1.7500 | 1,5000 | - | 12128 б |
Repodb | Queryfield | Почта | 148.31 США | 1.742 США | 2.633 США | 2.0000 | 1,5000 | 0,5000 | 13938 б |
Норм | 'Читать <> (кортежи)' | Valuetuple `8 | 148,58 США | 2.172 США | 3.283 США | 2.0000 | 1.7500 | - | 12745 б |
Норм | 'Read <()> (названные кортежи)' | Valuetuple `8 | 150,60 США | 0,658 США | 1.106 США | 2.2500 | 2.0000 | 1.2500 | 14562 б |
Repodb | Запрос | Почта | 152,34 США | 2.164 США | 3.271 США | 2.2500 | 1,5000 | 0,2500 | 14106 б |
Repodb | QueryDynamic | Почта | 154,15 США | 4.108 США | 6.210 США | 2.2500 | 1.7500 | 0,5000 | 13930 б |
Repodb | Запросы везда | Почта | 155,90 США | 1.953 США | 3.282 США | 2,5000 | 0,5000 | - | 14858 б |
Dapper Cache воздействие | ExecuteNoparameters_nocache | Пустота | 162,35 США | 1.584 США | 2.394 США | - | - | - | 760 б |
Dapper Cache воздействие | ExecuteNoparameters_cache | Пустота | 162.42 США | 2.740 США | 4.142 США | - | - | - | 760 б |
Dapper Cache воздействие | QueryFirstnoparameters_cache | Пустота | 164,35 США | 1.206 США | 1.824 США | 0,2500 | - | - | 1520 б |
Devexpress.xpo | Найти | Почта | 165,87 США | 1.012 США | 1.934 США | 8,5000 | - | - | 28099 б |
Dapper Cache воздействие | QueryFirstnoparameters_nocache | Пустота | 173,87 США | 1.178 США | 1.781 США | 0,5000 | - | - | 1576 б |
LINQ в DB | Первый | Почта | 175.21 США | 2.292 США | 3.851 США | 2.0000 | 0,5000 | - | 14041 б |
EF 6 | SQLQUERY | Почта | 175.36 США | 2.259 США | 3.415 США | 4.0000 | 0,7500 | - | 24209 б |
Норм | 'Читать <> (класс)' | Почта | 186.37 США | 1.305 США | 2.496 США | 3.0000 | 0,5000 | - | 17579 б |
Devexpress.xpo | GetObjectBykey | Почта | 186.78 США | 3.407 США | 5.151 США | 4,5000 | 1.0000 | - | 30114 б |
Окрашивать | 'Запрос (не разбитый)' | динамика | 194.62 США | 1.335 США | 2.019 США | 1.7500 | 1,5000 | - | 12048 б |
Окрашивать | 'Запрос (не разбитый)' | Почта | 195.01 США | 0,888 США | 1.343 США | 2.0000 | 1,5000 | - | 12008 б |
Devexpress.xpo | Запрос | Почта | 199,46 США | 5,500 США | 9.243 США | 10.0000 | - | - | 32083 б |
Белград | Firstordefault | Задание 1 | 228,70 США | 2.181 США | 3.665 США | 4,5000 | 0,5000 | - | 20555 б |
EF Core | 'Первый (скомпилирован)' | Почта | 265.45 США | 17.745 США | 26.828 США | 2.0000 | - | - | 7521 б |
Nhibernate | Получать | Почта | 276.02 США | 8.029 США | 12.139 США | 6,5000 | 1.0000 | - | 29885 б |
Nhibernate | HQL | Почта | 277.74 США | 13.032 США | 19.703 США | 8.0000 | 1.0000 | - | 31886 б |
Nhibernate | Критерии | Почта | 300.22 США | 14.908 США | 28.504 США | 13.0000 | 1.0000 | - | 57562 б |
EF 6 | Первый | Почта | 310,55 США | 27.254 США | 45,799 США | 13.0000 | - | - | 43309 б |
EF Core | Первый | Почта | 317.12 США | 1.354 США | 2.046 США | 3,5000 | - | - | 11306 б |
EF Core | SQLQUERY | Почта | 322,34 США | 23.990 США | 40.314 США | 5.0000 | - | - | 18195 б |
Nhibernate | SQL | Почта | 325,54 США | 3.937 США | 7.527 США | 22.0000 | 1.0000 | - | 80007 б |
EF 6 | 'First (без отслеживания)' | Почта | 331.14 США | 27.760 США | 46.649 США | 12.0000 | 1.0000 | - | 50237 б |
EF Core | 'First (без отслеживания)' | Почта | 337,82 США | 27.814 США | 46.740 США | 3.0000 | 1.0000 | - | 17986 б |
Nhibernate | Линейный | Почта | 604.74 США | 5.549 США | 10.610 США | 10.0000 | - | - | 46061 б |
Dapper Cache воздействие | ExecuteParameters_nocache | Пустота | 623,42 США | 3.978 США | 6.684 США | 3.0000 | 2.0000 | - | 10001 б |
Dapper Cache воздействие | QueryFirstParameters_nocache | Пустота | 630,77 США | 3.027 США | 4.576 США | 3.0000 | 2.0000 | - | 10640 б |
Не стесняйтесь отправлять патчи, которые включают в себя другие ORM - при запуске тестов, обязательно компилируйтесь в выпуске, а не прикрепите отладчик ( Ctrl + F5 ).
В качестве альтернативы, вы можете предпочесть тестовый набор Frans Bouma's 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 ) ;
Когда объект, который реализует интерфейс IDynamicParameters
передается в функции Execute
или Query
, значения параметров будут извлечены через этот интерфейс. Очевидно, что наиболее вероятным классом объекта для этой цели будет встроенный класс 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 поддерживает буквальные замены для Bool и Numeric Tipes.
connection . Query ( " select * from User where UserTypeId = {=Admin} " , new { UserTypeId . Admin } ) ;
Литеральная замена не отправляется в качестве параметра; Это позволяет лучшие планы и отфильтрованное использование индекса, но обычно следует использовать экономно и после тестирования. Эта функция особенно полезна, когда впрыскиваемое значение на самом деле является фиксированным значением (например, фиксированный «идентификатор категории», «код состояния» или «область», которая специфична для запроса). Для живых данных, где вы рассматриваете литералы, вы также можете рассмотреть и тестировать подсказки запросов, такие как OPTIMIZE FOR UNKNOWN
с обычными параметрами.
Поведение Dapper по умолчанию состоит в том, чтобы выполнить ваш SQL и буферизировать весь читатель при возврате. В большинстве случаев это идеально, поскольку минимизирует общие замки в БД и сокращает время сети БД.
Однако при выполнении огромных запросов вам может потребоваться минимизировать следы памяти и загружать только объекты по мере необходимости. Для этого пройти, 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
.
Это вариант использования для многопоставления. Вы говорите, что запрос возвращает Post
и User
объект, а затем дает ему функцию, описывающую, что вы хотите делать с каждым из строк, содержащих как Post
, так и User
объект. В нашем случае мы хотим взять объект пользователя и поместить его в объект Post. Итак, мы пишем функцию:
( post , user ) => { post . Owner = user ; return post ; }
Аргументы 3 типа в методе Query
указывают, какие объекты 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
, используйте необязательный параметр 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, если вы выполняете предложение, где на столбце VARCHAR с помощью параметра Обязательно передайте его таким образом:
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 собственность для карты.
Информация о кашах о кашах о каждом заполнении, которое он запускает, это позволяет быстро реализовать объекты и быстро обрабатывать параметры. Текущая реализация кэширует эту информацию в объекте ConcurrentDictionary
. Заявления, которые используются только один раз, обычно промываются из этого кеша. Тем не менее, если вы генерируете строки SQL на лету без использования параметров, это возможно, вы можете возникнуть в вопросах памяти.
Простота Dapper означает, что многие функции, которые отправляются, выпускаются. Он беспокоится о сценарии 95% и дает вам инструменты, которые вам нужны большую часть времени. Это не пытается решить каждую проблему.
Dapper не имеет подробной информации о реализации DB, он работает для всех поставщиков ADO .NET, включая SQLite, SQL CE, Firebird, Oracle, MariaDB, MySQL, PostgreSQL и SQL Server.
Dapper имеет комплексный набор тестов в тестовом проекте.
Dapper использует производственное использование при переполнении стека.