This document discusses the data access library Dapper and how it provides a simple yet high-performance way to query and manipulate data in databases. It begins by covering traditional data access methods in .NET and issues with ORMs. It then introduces Dapper as a micro-ORM that maps database rows to objects quickly using dynamic code generation. Key features covered include querying, loading related entities, paging results, and basic CRUD operations. The document encourages further reading on these topics.
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Advanced .NET Data Access with Dapper
1. Advanced Data Access
with Dapper
@Dave_Paquette
Microsoft MVP (ASP.NET/IIS)
contactme@davepaquette.com
http://www.davepaquette.com
2.
3. Data Access
How did we get here?
Why is this so hard?
public class Foo
{
//…some properties
}
SQL Server
4. Data Access in .NET
• In the beginning there was System.Data
• IDbConnection
• IDbCommand
• IDataReader
• IDbTransaction
5. Sytem.Data - Reading Records
var results = new List<Employee>();
using (SqlConnection connection = new SqlConnection(Settings.ConnectionString))
{
SqlCommand command = new SqlCommand(“SELECT * FROM Employees”, connection);
connection.Open();
IDataReader reader = command.ExecuteReader();
while (reader.Read())
{
results.Add(ReadSingleRow(reader));
}
reader.Close();
}
return results;
6. The ORMs will save us
• Hide details of database
• Generate SQL based on object model and configuration
• ChangeTracking
• Complex mapping strategies
• Many-to-many relationships
• Inheritance
8. ORM Pain Points
• Black box code generation –What is going on?
• Performance Problems
• Eager Loading vs Lazy Loading
• Complex Inheritance Chains
• Dealing with disconnected entities (in a web context)
9. The Micro-ORMs will save us!
• Query --> Objects
• Usually focused on speed
• Light on features
11. Dapper
• Used in production at Stack Exchange
• Super fast and easy to use
12. Performance of SELECT mapping over 500
iterations - POCO serialization
https://github.com/StackExchange/Dapper#performance
ORM Method Mean Allocated
LINQ to DB 'First (Compiled)' 78.75 us 2.66 KB
LINQ to DB Query<T> 80.38 us 6.87 KB
Hand Coded SqlCommand 87.16 us 12.24 KB
Dapper QueryFirstOrDefault<dynamic> 87.80 us 13.5 KB
Dapper QueryFirstOrDefault<T> 91.51 us 13.46 KB
Massive 'Query (dynamic)' 96.18 us 14.19 KB
PetaPoco 'Fetch<T> (Fast)' 96.57 us 13.65 KB
EF 6 SqlQuery 143.86 us 27.86 KB
EF Core 'First (Compiled)' 148.42 us 16.08 KB
NHibernate Get<T> 196.88 us 32.5 KB
EF Core First 197.91 us 20.25 KB
NHibernate HQL 207.84 us 35 KB
EF Core 'First (NoTracking)' 213.58 us 21.36 KB
13. How can it be that fast?
• Dapper dynamically writes code for you
• Emits IL code for tasks like loading a data
reader into an object
• https://github.com/StackExchange/Dapper/blo
b/master/Dapper/SqlMapper.cs#L3078
14. Dapper works on Database Connections
A set of extension methods on IDbConnection
Aircraft aircraft;
using (var connection = new SqlConnection(_connectionString))
{
await connection.OpenAsync(); //Optional
var query = "SELECT * FROM Aircraft WHERE Id = @Id";
Aircraft aircraft = await connection.QuerySingleAsync<Aircraft>(query, new {Id = id});
}
16. Loading Related Objects – Multi-Mapping
• Write a single query that returns all the data in a single row
scheduledFlights =
await connection.QueryAsync<ScheduledFlight, Airport, ScheduledFlight>(query,
(flight, airport) => {
flight.Airport = airport;
return flight;
},
new{FromCode = from} );
17. Multi-Mapping Caveats
• Data duplication
• Query returns a bloated data set
• Multiple instances of an object that represent the same thing
• This is totally fine forOne-to-One relationships
• No duplication here
18. Loading Related Objects – Multiple Queries
• Get data using multiple queries and wire them up yourself
• Still executed in a single command that returns multiple results sets
using (var multi = await connection.QueryMultipleAsync(query, new{FromCode = from} ))
{
scheduledFlights = multi.Read<ScheduledFlight>();
var airports = multi.Read<Airport>().ToDictionary(a => a.Id);
foreach(var flight in scheduledFlights)
{
flight.Airport = airports[flight.AirportId];
}
}
19. Multi-Mapping vs Multiple Queries
100 ScheduledFlight Records 1,000 ScheduledFlight Records
Method Mean
MultiMapping 926.5 us
MultipleResultSets 705.9 us
Method Mean
MultiMapping 5.098 ms
MultipleResultSets 2.809 ms
https://www.davepaquette.com/archive/2018/04/10/loading-related-entities-many-to-one-part-2.aspx
22. Paging through large collections
• Use an OFFSET FETCH query
• Include a total count in the result
SELECT * FROM Flight f
INNER JOIN ScheduledFlight sf
ON f.ScheduledFlightId = sf.Id
ORDER BY Day, FlightNumber
OFFSET 0 ROWS
FETCH NEXT 10 ROWS ONLY
SELECT COUNT(*) FROM Flight f
INNER JOIN ScheduledFlight sf
ON f.ScheduledFlightId = sf.Id
23. Insert / Update / Delete
• Use the ExecuteAsync method
• Built-in support for batch inserts
await connection.ExecuteAsync(
@"INSERT INTO Flight(ScheduledFlightId, Day, ScheduledDeparture,
ScheduledArrival)
VALUES(@ScheduledFlightId, @Day, @ScheduledDeparture, @ScheduledArrival)",
flights);
Data access has consumed such a huge amount of our time.
- Why is that?
- What is so hard about this?
These 4 interfaces were in the original .NET framework.
They have been the basis for ALL database access since then.
Each database provider has an implementation of each of these interfaces (eg. SqlConnection)
Every ORM or Micro-ORM written in .NET is based on these 4 interfaces.
Show sample code for reading and writing data using the raw SQL
Swinging the pendulum
Demo
Start with loading simply the first level (Airport + Arrival and Departure Flights)
Using multi-mapping to load the ScheduledFlight info
Use multi queries to also load each ScheduledFlight’s Arrival and Departure airports