Introduction
Dapper is a simple object mapper for .NET data access which uses Microsoft classes under the covers which has been covered in the following article Using Dapper - C# Part 1 which is part of a series on Dapper.
Recently there has been a minor change with working with stored procedures.
The former method for calling a stored procedure using Dapper the command type was required as shown below with commandType: CommandType.StoredProcedure.
private static async Task GetAllEmployees()
{
await using SqlConnection cn = new(DataConnections.Instance.MainConnection);
// get employees via a stored procedure
var employees =
(
await cn.QueryAsync<Employee>("usp_GetAllEmployees",
commandType: CommandType.StoredProcedure)
)
.AsList();
}
Now a developer has a little less coding as the command type is not required.
private static async Task GetAllEmployees()
{
await using SqlConnection cn = new(DataConnections.Instance.MainConnection);
// get employees via a stored procedure
var employees =
(
await cn.QueryAsync<Employee>("usp_GetAllEmployees")
)
.AsList();
}
Code
To try out the above clone the following repository.
- Under LocalDb create a database named DapperStoredProcedures
- Run Scripts\populate.sql
- Run the project
- GetAllEmployees method returns all records
- GetEmployeeByGender method returns records by gender using an enum.
Note
Since Dapper does not handle DateOnly the following package kp.Dapper.Handlers is used.
using Dapper;
using DapperStoredProcedures1.Classes;
using DapperStoredProcedures1.Models;
using Dumpify;
using kp.Dapper.Handlers;
using Microsoft.Data.SqlClient;
namespace DapperStoredProcedures1;
internal partial class Program
{
static async Task Main(string[] args)
{
await Setup();
// Allows Dapper to handle DateOnly types
SqlMapper.AddTypeHandler(new SqlDateOnlyTypeHandler());
await GetAllEmployees();
Console.WriteLine();
await GetEmployeeByGender();
ExitPrompt();
}
private static async Task GetEmployeeByGender()
{
AnsiConsole.MarkupLine("[cyan]Female employees[/]");
await using SqlConnection cn = new(DataConnections.Instance.MainConnection);
// get employees via a stored procedure
var employees =
(
await cn.QueryAsync<Employee>("usp_GetEmployeeByGender",
param: new { GenderId = Genders.Female })
)
.AsList();
// Nicely display the results from the stored procedure
employees.Dump();
}
private static async Task GetAllEmployees()
{
AnsiConsole.MarkupLine("[cyan]All employees[/]");
await using SqlConnection cn = new(DataConnections.Instance.MainConnection);
// get employees via a stored procedure
var employees =
(
await cn.QueryAsync<Employee>("usp_GetAllEmployees")
)
.AsList();
// Nicely display the results from the stored procedure
employees.Dump();
}
}
Summary
Now a developer has just a little less code to write when working with Dapper and stored procedures. If for some reason this does not work, report this to the Dapper team here.
Also, although code provided uses SQL-Server, this will work with any data provider which supports stored procedures.
Top comments (3)
If you don't pass a CommandType, then Dapper has to infer it. It checks if your SQL is a single word and presumes it's the name of a stored procedure unless it's in an exclusion list of single-word commands of type text (e.g., commit, rollback, revert, vacuum).
If I know the command I'm passing is the name of a stored procedure, then I'd prefer to pass CommandType: StoredProcedure. Then your program (including Dapper) does less work.
Of course you can pass the command type. And if you look at the Dapper source code there really is nothing lost work-wise.
just out of curiosity, I ran this using BenchmarkDotNet. My "usp_dummy" stored procedure simply does a "SELECT 1". I have a SQL Server instance running on my local machine. Not exactly the best test to isolate Dapper's inference logic.
The Explicit method was always faster and usually by 5-6% (about 4 microseconds). So I wouldn't say the cost of reading/writing less code is completely free. If the difference was in nano seconds, I probably wouldn't care. I do like that Dapper infers it if it's omitted but that omission comes at a cost.