DEV Community

Davide Mauri
Davide Mauri

Posted on • Edited on • Originally published at Medium

Get started with Dapper .NET

Dapper is a micro ORM born in 2011. You can still find the original post online, here.

It is one of the fastest and simplest around, yet it is very extensible and, above all, well adopted and used in very high-performance websites. More specifically has been developed and is maintained by the guys behind StackOverflow, so you can be sure it is battle-tested. Source code is on GitHub

StackExchange/Dapper

and development is very active. All these reasons made it my micro ORM of choice.

Setting up the environment

Since I use Azure SQL or SQL Server for my daily work I’ll just use that database for all the examples. If you’re on a different platform than Windows, just use the SQL Server 2019 Docker image. Here you can find anything you need to know to get started with SQL Server and Docker.

Get started with SQL Server on Docker

For the purpose of becoming familiar with other platforms, I’m writing this series of articles using all the three major platforms: macOS, Linux (Ubuntu) and Windows 10. This means that I’m also using .NET Core and Visual Studio Code.

Installing Dapper

Dapper supports both .NET and .NET Core. To start with Dapper you just need to install the NuGet or dotnet package:

Dapper

Let’s start querying!

As said in the subtitle, in this article I’ll focus on the basics of Dapper .NET, and all it takes to get familiar with it are just 10 samples I have created and posted on GitHub, here:

yorek/dapper-samples

Dapper .NET extends the IDBConnection interface, adding four methods:

  • Query
  • Execute
  • ExecuteScalar
  • ExecuteReader

they are all defined in SqlMapper.cs and, they all supporting async calls, via the specialized <MethodName> methods. The 10 samples I’ve created shows how the above methods can be used. Let’s take a look at them before playing with the sample code:

Query

Executes a query and maps the result to a list of dynamic objects or, if specified, to a list of strongly typed objects:

public class User
{
  public int Id { get; set; }
  public string FirstName { get; set; }
  public string LastName { get; set; }
}

// conn is a SqlConnection
var queryResult = conn.Query<User>("SELECT [Id], [FirstName],[LastName] FROM dbo.[Users]");
Enter fullscreen mode Exit fullscreen mode

If the target object has been specified (“User” in the above sample), mapping is done by matching the names of returned columns with object properties names.

Since the result is an IEnumerable, you can use First(), FirstOrDefault(), Single() or SingleOrDefault() if you need to, but you don’t really have to, since there are specialized implementations of the Query method that already do this for you:

  • QueryFirst
  • QueryFirstOrDefault
  • QuerySingle
  • QuerySingleOrDefault

Here’s a summary what you can expect to be returned by each method in case your query return no rows, one row or multiple rows:

Alt Text

Of course, if you have asked Dapper to return a specific type, like the User in the sample code shown before, you will have the requested object returned instead of “Row”.

Generally speaking, you should prefer using the above methods instead of using the one with the same names on the IEnumerable. This is due to how Dapper works behind the scenes: by default the entire result set is read in memory and the returned as an IEnumerable. The idea behind this behavior is to keep the connection to the database open for the least amount of time possible, in order to favor concurrency at the database level. If you don’t like this behavior, keep in mind that it can be changed and we’ll discuss about that in future. Anyway, this means that if you only need one row or you’re expecting only one row, there is no point in loading everything in memory, and only then taking the first row or checking if you got exactly a single row, right? That’s why the methods provided by Dapper should be preferred. They will just take one row, without loading all the others in memory. It may seem a very small thing, but are those kind of details that make performance great instead of just good.

Execute

This method executes a query that doesn’t return any resultset. Its return value contains that number of rows affected by the query. If the executed actually generates a resultset, that will be discarded.

int affectedRows = conn.Execute("UPDATE dbo.[Users] SET [FirstName] = 'John' WHERE [Id] = 3");
Enter fullscreen mode Exit fullscreen mode

ExecuteScalar

This methods executes a query that return a resultset made of exactly one row and one column. Just like the good old IDBCommand.ExecuteScalar.

conn.ExecuteScalar<string>("SELECT [FirstName] FROM dbo.[Users] WHERE [Id] = 1")
Enter fullscreen mode Exit fullscreen mode

ExecuteReader

I think that this method exists mainly for making easier to adopt Dapper in legacy codebases where instead of dealing with strongly typed lists you have to deal with a DataReader, and refactoring to use a typed list it is not possible. If that’s the case, then is the method for you. As the name suggest it just returns a IDataReader.

var dataReader = conn.ExecuteReader("SELECT [Id], [FirstName], [LastName] FROM dbo.[Users]");
Enter fullscreen mode Exit fullscreen mode

Parameters

All the aforementioned methods supports parametric queries. Parameters are defined in queries using the “@” symbol and then there are two ways that can be used to assign values to defined parameters: using anonymous objects or the Dapper-specific DynamicParameter class.

Parameters via Anonymous Objects

With this option all is needed is that anonymous object’s fields have the same name of the used parameters. You can use the “@” sign also in the field name if you want or you can omit it as long as, again, the name matches the parameter you want to assign the value to.

var queryResult = conn.Query<User>(“SELECT [Id], [FirstName], [LastName] FROM dbo.[Users] WHERE Id = @Id”, new { @Id = 1 });
Enter fullscreen mode Exit fullscreen mode

This is very concise and clean, but it doesn’t allow to specify if a parameter is an input or output parameter, and the data type is inferred from the parameter type itself. Here’s what get executed on the database:

Alt Text

So, while very convenient, this behavior could drive to unpleasant implicit conversions, especially with strings that are sent as nvarchar(4000) by default. If you want have control on how strings are passed to the database, you have to use the DBString class:

Alt Text

One nice feature that you have when using anonymous objects as parameters is the ability to pass an IEnumerable. This is useful in case you need to pass a list of values to be used in a IN construct: can just pass an IEnumerable as parameter value, and the values will be automatically expanded to a comma-separated list of values.

Alt Text

Parameters via DynamicParameter

By using the DynamicParameters object all options related to a parameter can be set.

DynamicParameters dp = new DynamicParameters();
dp.Add("FirstName", "Davide", DbType.String,   
   ParameterDirection.Input, 100);
dp.Add("LastName", "Mauri");

var queryResult = conn.Query<User>("SELECT [Id], [FirstName], [LastName] FROM dbo.[Users] WHERE FirstName = @FirstName AND LastName = @LastName", dp);
Enter fullscreen mode Exit fullscreen mode

You have full control over how parameters are sent to the database, as you can see from what get executed:

Alt Text

Dynamic Parameters are especially perfect for complex stored procedure, in case you need handle also a return value:

Alt Text

Stored Procedures

Of course execution of stored procedure is supported. Any of the mentioned methods allows the execution of a stored procedure. All it’s needed is to omit the EXEC command in the query and specify the StoredProcedure command type:

conn.Query<User>(
    "dbo.ProcedureBasic", 
    new { @email = "info@davidemauri.it" }, 
    commandType: CommandType.StoredProcedure
);
Enter fullscreen mode Exit fullscreen mode

That’s it

This all you need to get started with Dapper .NET. Working samples of all the aforementioned methods are available on GitHub:

yorek/dapper-samples

What’s Next?

Next topic will be about a Dapper feature called “Multiple Execution”

Top comments (0)