C# Dapper
last modified October 18, 2026
In this article we show how to program databases in C# with Dapper. In the examples we work with PostgreSQL.
Dapper
Dapper is a simple object mapper for the .NET platform. It is a micro-ORM: it adds
extension methods to IDbConnection that run SQL and map the results to
objects. Unlike a full ORM such as Entity Framework Core, it does not generate SQL,
track changes, or manage a schema. We write the SQL, and Dapper takes care of the
parameters and the mapping.
Dapper's goals are code simplicity and performance. It was created by the team at Stack Overflow.
Dapper has no database-specific implementation details; it works across all .NET ADO.NET providers, including SQLite, Firebird, Oracle, MySQL, PostgreSQL, and SQL Server.
$ dotnet add package Dapper
To use Dapper, we add the package reference to the project with the
dotnet tool.
$ dotnet add package Npgsql
We also add the driver for PostgreSQL. The examples need a server with a
testdb database; the easiest way is a Docker container.
$ docker run -d --name pg -e POSTGRES_PASSWORD=passwd -e POSTGRES_DB=testdb \
-p 5432:5432 postgres:18
DROP TABLE IF EXISTS orders;
DROP TABLE IF EXISTS cars;
DROP TABLE IF EXISTS customers;
CREATE TABLE cars(id serial PRIMARY KEY, name VARCHAR(255), price INT);
INSERT INTO cars(name, price) VALUES('Audi', 52642);
INSERT INTO cars(name, price) VALUES('Mercedes', 57127);
INSERT INTO cars(name, price) VALUES('Skoda', 9000);
INSERT INTO cars(name, price) VALUES('Volvo', 29000);
INSERT INTO cars(name, price) VALUES('Bentley', 350000);
INSERT INTO cars(name, price) VALUES('Citroen', 21000);
INSERT INTO cars(name, price) VALUES('Hummer', 41400);
INSERT INTO cars(name, price) VALUES('Volkswagen', 21600);
CREATE TABLE customers(id serial PRIMARY KEY, full_name VARCHAR(255));
INSERT INTO customers(full_name) VALUES('John Doe'), ('Jane Doe');
CREATE TABLE orders(id serial PRIMARY KEY,
customer_id INT REFERENCES customers(id),
car_id INT REFERENCES cars(id),
order_date DATE);
INSERT INTO orders(customer_id, car_id, order_date) VALUES
(1, 1, '2026-01-10'), (1, 3, '2026-02-11'), (2, 2, '2026-03-12');
In our examples, we use these tables. The examples run one after another and change the data, so the outputs below follow that order; run the script again to start from the beginning.
C# Dapper ExecuteScalar
The ExecuteScalar method executes a query that selects a single value.
using Dapper;
using Npgsql;
string cs = "User ID=postgres;Password=passwd;Host=localhost;Database=testdb";
using var con = new NpgsqlConnection(cs);
con.Open();
var ver = con.ExecuteScalar<string>("SELECT version()");
Console.WriteLine(ver);
int count = con.ExecuteScalar<int>("SELECT count(*) FROM cars");
Console.WriteLine(count);
The example retrieves the version of the PostgreSQL database and the number of cars.
string cs = "User ID=postgres;Password=passwd;Host=localhost;Database=testdb";
We provide the connection string for the PostgreSQL database. In a real application, the password comes from configuration or a secret store.
using var con = new NpgsqlConnection(cs); con.Open();
We create a new NpgsqlConnection and open it. Dapper opens a closed
connection by itself for the duration of a call, but an explicit
Open is needed for transactions and avoids reopening it.
var ver = con.ExecuteScalar<string>("SELECT version()");
The ExecuteScalar method executes the query, which returns a single
value, and converts it to the requested type.
$ dotnet run PostgreSQL 18.6 (Debian 18.6-1.pgdg13+2) on x86_64-pc-linux-gnu, compiled by gcc (Debian 14.2.0-19) 14.2.0, 64-bit 8
C# Dapper Query
The Query method executes a query and maps the rows to a sequence of
objects. With a type argument, the rows are mapped to that type; without one, they
are mapped to dynamic objects.
using Dapper;
using Npgsql;
string cs = "User ID=postgres;Password=passwd;Host=localhost;Database=testdb";
using var con = new NpgsqlConnection(cs);
con.Open();
var cars = con.Query<Car>("SELECT * FROM cars").ToList();
cars.ForEach(car => Console.WriteLine(car));
// without a type, Query returns dynamic objects
foreach (var row in con.Query("SELECT name, price FROM cars WHERE price > 100000"))
{
Console.WriteLine($"{row.name} {row.price}");
}
record Car(int Id, string Name, int Price);
The example retrieves all rows from the cars table, and then a subset
as dynamic objects.
var cars = con.Query<Car>("SELECT * FROM cars").ToList();
The Query method executes the statement and returns the objects. Dapper
matches the column names to the property names or to the constructor parameters of
the record, ignoring the case. By default, the result is buffered into a list, so
the connection is free immediately.
foreach (var row in con.Query("SELECT name, price FROM cars WHERE price > 100000"))
{
Console.WriteLine($"{row.name} {row.price}");
}
For a quick look at the data, dynamic rows are convenient. The columns are properties of the row, but without compile-time checking.
$ dotnet run
Car { Id = 1, Name = Audi, Price = 52642 }
Car { Id = 2, Name = Mercedes, Price = 57127 }
Car { Id = 3, Name = Skoda, Price = 9000 }
Car { Id = 4, Name = Volvo, Price = 29000 }
Car { Id = 5, Name = Bentley, Price = 350000 }
Car { Id = 6, Name = Citroen, Price = 21000 }
Car { Id = 7, Name = Hummer, Price = 41400 }
Car { Id = 8, Name = Volkswagen, Price = 21600 }
Bentley 350000
C# Dapper ExecuteReader
The ExecuteReader method executes the SQL and returns an
IDataReader. This is typically used when the results of a query are not
processed by Dapper, for example, to fill a DataTable or a
DataSet.
using Dapper;
using Npgsql;
string cs = "User ID=postgres;Password=passwd;Host=localhost;Database=testdb";
using var con = new NpgsqlConnection(cs);
con.Open();
using (var reader = con.ExecuteReader("SELECT id, name, price FROM cars ORDER BY id LIMIT 3"))
{
while (reader.Read())
{
int id = reader.GetInt32(0);
string name = reader.GetString(1);
int price = reader.GetInt32(2);
Console.WriteLine($"{id} {name} {price}");
}
}
// the reader can fill a DataTable
using var reader2 = con.ExecuteReader("SELECT id, name FROM cars");
var table = new System.Data.DataTable();
table.Load(reader2);
Console.WriteLine($"{table.Rows.Count} rows, {table.Columns.Count} columns");
In the example, we retrieve rows from the table with ExecuteReader, and
load all rows into a DataTable.
using (var reader = con.ExecuteReader("SELECT id, name, price FROM cars ORDER BY id LIMIT 3"))
{
while (reader.Read())
{
Here, no mapping is done by Dapper. We process the columns manually, by their ordinal positions. The reader must be disposed before the next command is run on the same connection; PostgreSQL allows only one active reader per connection, which is why the first reader is in its own block.
$ dotnet run 1 Audi 52642 2 Mercedes 57127 3 Skoda 9000 8 rows, 2 columns
C# Dapper Execute
The Execute method executes an SQL statement. It is used for INSERT,
UPDATE, and DELETE statements.
using Dapper;
using Npgsql;
string cs = "User ID=postgres;Password=passwd;Host=localhost;Database=testdb";
using var con = new NpgsqlConnection(cs);
con.Open();
int affectedRows = con.Execute("UPDATE cars SET price = 52000 WHERE id = 1");
Console.WriteLine($"UPDATE affected rows: {affectedRows}");
int none = con.Execute("UPDATE cars SET price = 1 WHERE id = 999");
Console.WriteLine($"UPDATE affected rows: {none}");
The example updates the price of a car, and then tries to update a row that does not exist.
int affectedRows = con.Execute("UPDATE cars SET price = 52000 WHERE id = 1");
The UPDATE statement changes the price of a car. The Execute method
returns the number of affected rows. Zero tells us that no row matched.
$ dotnet run UPDATE affected rows: 1 UPDATE affected rows: 0
C# Dapper parameterized query
Parameterized queries increase security and performance. When we write parameterized queries, we use placeholders instead of directly writing the values into the queries. Never build SQL by concatenating or interpolating values; that is how SQL injection happens. Dapper takes the parameters from the properties of an object, usually an anonymous one.
using Dapper;
using Npgsql;
string cs = "User ID=postgres;Password=passwd;Host=localhost;Database=testdb";
using var con = new NpgsqlConnection(cs);
con.Open();
var car = con.QueryFirst<Car>("SELECT * FROM cars WHERE id = @id", new { id = 3 });
Console.WriteLine(car);
var cars = con.Query<Car>("SELECT * FROM cars WHERE id IN (@id1, @id2)", new { id1 = 1, id2 = 2 });
Console.WriteLine(string.Join("\n", cars));
// QueryFirstOrDefault returns null when nothing is found
var none = con.QueryFirstOrDefault<Car>("SELECT * FROM cars WHERE id = @id", new { id = 999 });
Console.WriteLine(none is null);
// Single throws unless there is exactly one row
try
{
con.QuerySingle<Car>("SELECT * FROM cars WHERE price > @p", new { p = 1 });
}
catch (InvalidOperationException e)
{
Console.WriteLine(e.Message);
}
record Car(int Id, string Name, int Price);
The example selects specific rows from the table and shows the different
Query... methods.
var car = con.QueryFirst<Car>("SELECT * FROM cars WHERE id = @id", new { id = 3 });
The QueryFirst method returns the first row of the result. The
@id is a placeholder, and the anonymous object provides its value.
var none = con.QueryFirstOrDefault<Car>("SELECT * FROM cars WHERE id = @id", new { id = 999 });
The OrDefault variant returns null when no row is found; the
plain QueryFirst throws in that case.
con.QuerySingle<Car>("SELECT * FROM cars WHERE price > @p", new { p = 1 });
The Single methods demand exactly one row (or at most one, for
SingleOrDefault) and throw an InvalidOperationException
otherwise. This is how to express "this query must identify one row".
$ dotnet run
Car { Id = 3, Name = Skoda, Price = 9000 }
Car { Id = 1, Name = Audi, Price = 52000 }
Car { Id = 2, Name = Mercedes, Price = 57127 }
True
Sequence contains more than one element
C# Dapper list parameters
A common need is a condition such as "the id is one of these". For PostgreSQL, the
Npgsql driver passes .NET arrays directly, so we use = ANY(@ids). Dapper
also expands list parameters for providers that do not have array parameters: there
we write IN @ids.
using Dapper;
using Npgsql;
string cs = "User ID=postgres;Password=passwd;Host=localhost;Database=testdb";
using var con = new NpgsqlConnection(cs);
con.Open();
int[] ids = [1, 2, 5];
// Npgsql passes arrays natively, so ANY works; other databases expand "IN @ids"
var cars = con.Query<Car>("SELECT * FROM cars WHERE id = ANY(@ids)", new { ids });
Console.WriteLine(string.Join(", ", cars.Select(c => c.Name)));
string[] names = ["Audi", "Volvo"];
var byName = con.Query<Car>("SELECT * FROM cars WHERE name = ANY(@names) ORDER BY name", new { names });
Console.WriteLine(string.Join(", ", byName.Select(c => c.Id)));
// LIKE patterns belong in the parameter value, not in the SQL
var like = con.Query<Car>("SELECT * FROM cars WHERE name LIKE @pattern", new { pattern = "V%" });
Console.WriteLine(string.Join(", ", like.Select(c => c.Name)));
record Car(int Id, string Name, int Price);
The example selects cars by a list of ids and by a list of names, and uses a
LIKE pattern.
var like = con.Query<Car>("SELECT * FROM cars WHERE name LIKE @pattern", new { pattern = "V%" });
The wildcard characters belong in the value of the parameter, not in the SQL text. That way the value is still a parameter and cannot change the structure of the query.
$ dotnet run Audi, Mercedes, Bentley 1, 4 Volvo, Volkswagen
C# Dapper QueryMultiple
The QueryMultiple method executes a command that returns multiple result
sets and lets us read each in turn. It saves round trips to the database.
using Dapper;
using Npgsql;
string cs = "User ID=postgres;Password=passwd;Host=localhost;Database=testdb";
using var con = new NpgsqlConnection(cs);
con.Open();
var sql = @"SELECT * FROM cars WHERE id = @id1;
SELECT * FROM cars WHERE id = @id2;
SELECT * FROM customers";
using var multi = con.QueryMultiple(sql, new { id1 = 1, id2 = 2 });
var c1 = multi.Read<Car>().Single();
var c2 = multi.Read<Car>().Single();
var customers = multi.Read<Customer>().ToList();
Console.WriteLine(c1);
Console.WriteLine(c2);
Console.WriteLine(string.Join(", ", customers.Select(c => c.Full_Name)));
record Car(int Id, string Name, int Price);
record Customer(int Id, string Full_Name);
The example sends three SELECT statements in one go.
var sql = @"SELECT * FROM cars WHERE id = @id1;
SELECT * FROM cars WHERE id = @id2;
SELECT * FROM customers";
We define a multiple select statement. The statements are separated by a semicolon.
using var multi = con.QueryMultiple(sql, new { id1 = 1, id2 = 2 });
var c1 = multi.Read<Car>().Single();
var customers = multi.Read<Customer>().ToList();
The statements are executed with QueryMultiple, and the result sets are
read in the order of the statements. Each Read consumes one result set.
The GridReader must be disposed, which the using
declaration does.
$ dotnet run
Car { Id = 1, Name = Audi, Price = 52000 }
Car { Id = 2, Name = Mercedes, Price = 57127 }
John Doe, Jane Doe
C# Dapper DynamicParameters
The DynamicParameters class is a bag of parameters that can be passed
to the Dapper Query and Execute methods. It gives control
over the type, size, and direction of each parameter, and the parameters can be
added step by step.
using System.Data;
using Dapper;
using Npgsql;
string cs = "User ID=postgres;Password=passwd;Host=localhost;Database=testdb";
using var con = new NpgsqlConnection(cs);
con.Open();
var query = "INSERT INTO cars(name, price) VALUES(@name, @price) RETURNING id";
var dp = new DynamicParameters();
dp.Add("@name", "Lada", DbType.AnsiString, ParameterDirection.Input, 255);
dp.Add("@price", 36600);
int id = con.ExecuteScalar<int>(query, dp);
Console.WriteLine($"row inserted, id {id}");
// a parameter can be added in steps
var dp2 = new DynamicParameters();
dp2.Add("minPrice", 40000);
dp2.Add("maxPrice", 60000);
var cars = con.Query<string>("SELECT name FROM cars WHERE price BETWEEN @minPrice AND @maxPrice ORDER BY name", dp2);
Console.WriteLine(string.Join(", ", cars));
The example inserts a new row into the cars table and gets its id, and
then builds a query with parameters added separately.
var query = "INSERT INTO cars(name, price) VALUES(@name, @price) RETURNING id";
The RETURNING clause is a PostgreSQL feature that makes the INSERT
return the generated key. We read it with ExecuteScalar. Other
databases have their own ways, such as SELECT SCOPE_IDENTITY() in SQL
Server or last_insert_rowid() in SQLite.
dp.Add("@name", "Lada", DbType.AnsiString, ParameterDirection.Input, 255);
dp.Add("@price", 36600);
We provide the parameter values and, for the first one, the type, the direction, and the size. An explicit size lets the database reuse the plan of the statement.
$ dotnet run row inserted, id 9 Audi, Hummer, Mercedes
C# Dapper bulk insert
We can insert multiple rows with Execute. If the parameter is a list of
objects, Dapper runs the command once for every element.
using Dapper;
using Npgsql;
string cs = "User ID=postgres;Password=passwd;Host=localhost;Database=testdb";
using var con = new NpgsqlConnection(cs);
con.Open();
var stm = "INSERT INTO cars(name, price) VALUES(@name, @price)";
var data = new List<Car>
{
new Car(Name: "Lada 2", Price: 63444),
new Car(Name: "Toyota 2", Price: 63330)
};
// one command is executed for every element of the list
int n = con.Execute(stm, data);
Console.WriteLine($"{n} cars inserted");
Console.WriteLine(con.ExecuteScalar<int>("SELECT count(*) FROM cars"));
record Car(string Name, int Price);
The program inserts two cars into the table.
int n = con.Execute(stm, data);
The Execute method takes the statement and the list of objects as
parameters. The properties of the objects fill the placeholders. The returned value
is the total number of affected rows.
$ dotnet run 2 cars inserted 11
This is a loop of commands, not a single bulk operation, so it is not suitable for
a very large number of rows. In that case, use the bulk copy facility of the
provider; for PostgreSQL, Npgsql has binary import with
BeginBinaryImport. Wrapping the inserts in a transaction speeds them up
considerably.
C# Dapper transactions
A transaction is created on the connection and passed to the Dapper methods. If any statement fails, we roll everything back.
using Dapper;
using Npgsql;
string cs = "User ID=postgres;Password=passwd;Host=localhost;Database=testdb";
using var con = new NpgsqlConnection(cs);
con.Open();
using var tx = con.BeginTransaction();
try
{
con.Execute("UPDATE cars SET price = price - 1000 WHERE id = @id", new { id = 1 }, tx);
con.Execute("UPDATE cars SET price = price + 1000 WHERE id = @id", new { id = 2 }, tx);
// a statement that fails: the foreign key from orders blocks the delete
con.Execute("DELETE FROM cars WHERE id = @id", new { id = 3 }, tx);
tx.Commit();
}
catch (PostgresException e)
{
tx.Rollback();
Console.WriteLine($"rolled back: {e.MessageText}");
}
Console.WriteLine(con.QuerySingle<int>("SELECT price FROM cars WHERE id = 1"));
The program moves a price between two cars and then tries to delete a car that is referenced by an order. The delete fails and the whole transaction is rolled back.
con.Execute("UPDATE cars SET price = price - 1000 WHERE id = @id", new { id = 1 }, tx);
The transaction is the third argument of Execute. A command that is
not given the transaction fails on a connection that has an active one.
$ dotnet run rolled back: update or delete on table "cars" violates foreign key constraint "orders_car_id_fkey" on table "orders" 52000
The price of the Audi is still 52000, not 51000.
C# Dapper multi-mapping
A join returns columns of several tables in one row. Multi-mapping splits the row into several objects and lets us connect them.
using Dapper;
using Npgsql;
string cs = "User ID=postgres;Password=passwd;Host=localhost;Database=testdb";
using var con = new NpgsqlConnection(cs);
con.Open();
var sql = @"SELECT o.id, o.order_date, c.id, c.full_name, k.id, k.name, k.price
FROM orders o
JOIN customers c ON c.id = o.customer_id
JOIN cars k ON k.id = o.car_id
ORDER BY o.id";
// the splitOn columns tell Dapper where the next object starts
var orders = con.Query<Order, Customer, Car, Order>(sql,
(order, customer, car) =>
{
order.Customer = customer;
order.Car = car;
return order;
},
splitOn: "id,id");
foreach (var o in orders)
{
Console.WriteLine($"{o.Id}: {o.Customer!.Full_Name} bought {o.Car!.Name} on {o.Order_Date:d}");
}
class Order
{
public int Id { get; set; }
public DateTime Order_Date { get; set; }
public Customer? Customer { get; set; }
public Car? Car { get; set; }
}
class Customer
{
public int Id { get; set; }
public string Full_Name { get; set; } = "";
}
class Car
{
public int Id { get; set; }
public string Name { get; set; } = "";
public int Price { get; set; }
}
The program lists the orders with the customer and the car.
var orders = con.Query<Order, Customer, Car, Order>(sql,
(order, customer, car) =>
{
order.Customer = customer;
order.Car = car;
return order;
},
splitOn: "id,id");
The type arguments are the types of the objects in the row, in order, and the type of
the result. The function receives the objects built from each row and returns the
result. splitOn lists the columns at which a new object starts; the
default is Id, so here we state the two places explicitly: the second
id begins the customer and the third begins the car. The column names
must be in the same order as the types.
$ dotnet run 1: John Doe bought Audi on 1/10/2026 2: John Doe bought Skoda on 2/11/2026 3: Jane Doe bought Mercedes on 3/12/2026
When one object contains a list of others (one customer, many orders), the function must also gather the rows with the same parent, typically with a dictionary keyed by the parent id.
C# Dapper column mapping
The database uses full_name, while C# uses FullName. Dapper
does not convert the names by itself, but it can be told to ignore underscores, or
we can alias the columns.
using Dapper;
using Npgsql;
string cs = "User ID=postgres;Password=passwd;Host=localhost;Database=testdb";
using var con = new NpgsqlConnection(cs);
con.Open();
Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true;
var customers = con.Query<Customer>("SELECT id, full_name FROM customers").ToList();
customers.ForEach(Console.WriteLine);
// or alias the columns
var aliased = con.Query<Person>("SELECT id, full_name AS Name FROM customers").ToList();
aliased.ForEach(Console.WriteLine);
record Customer(int Id, string FullName);
record Person(int Id, string Name);
The program maps the full_name column in two ways.
Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true;
The global setting makes Dapper ignore underscores when matching names, so
full_name fills FullName. Set it once at the start of the
program. It works for properties and for the constructor parameters of records.
con.Query<Person>("SELECT id, full_name AS Name FROM customers")
An alias in the SQL is the simplest and most explicit way and works in all cases.
$ dotnet run
Customer { Id = 1, FullName = John Doe }
Customer { Id = 2, FullName = Jane Doe }
Person { Id = 1, Name = John Doe }
Person { Id = 2, Name = Jane Doe }
C# Dapper async and streaming
Every method has an asynchronous variant with the Async suffix. For big
result sets, we can avoid loading all rows into memory by passing
buffered: false, or by enumerating asynchronously.
using Dapper;
using Npgsql;
string cs = "User ID=postgres;Password=passwd;Host=localhost;Database=testdb";
using var con = new NpgsqlConnection(cs);
con.Open();
var cars = await con.QueryAsync<Car>("SELECT * FROM cars ORDER BY id LIMIT 3");
foreach (var car in cars)
{
Console.WriteLine(car);
}
var price = await con.ExecuteScalarAsync<int>("SELECT max(price) FROM cars");
Console.WriteLine(price);
// buffered: false streams the rows instead of loading all of them into a list
foreach (var car in con.Query<Car>("SELECT * FROM cars ORDER BY id", buffered: false).Take(2))
{
Console.WriteLine(car.Name);
}
// await foreach over an unbuffered query (Dapper 2.1)
await foreach (var car in con.QueryUnbufferedAsync<Car>("SELECT * FROM cars ORDER BY id LIMIT 2"))
{
Console.WriteLine(car.Name);
}
record Car(int Id, string Name, int Price);
The program uses the async methods and two ways of streaming rows.
foreach (var car in con.Query<Car>("SELECT * FROM cars ORDER BY id", buffered: false).Take(2))
With buffered: false, the rows are read from the database as the
sequence is enumerated. Stopping after two rows leaves the rest unread, but the
connection stays busy until the enumeration finishes or is disposed, so no other
command can run on it in the meantime.
await foreach (var car in con.QueryUnbufferedAsync<Car>("SELECT * FROM cars ORDER BY id LIMIT 2"))
QueryUnbufferedAsync returns an IAsyncEnumerable, which is
consumed with await foreach.
$ dotnet run
Car { Id = 1, Name = Audi, Price = 52000 }
Car { Id = 2, Name = Mercedes, Price = 57127 }
Car { Id = 3, Name = Skoda, Price = 9000 }
350000
Audi
Mercedes
Audi
Mercedes
C# Dapper functions and stored procedures
Databases with stored procedures (SQL Server, MySQL, Oracle) are called with
commandType: CommandType.StoredProcedure and the name of the procedure
in place of the SQL text. PostgreSQL has functions, which are called with an ordinary
SELECT.
using Dapper;
using Npgsql;
string cs = "User ID=postgres;Password=passwd;Host=localhost;Database=testdb";
using var con = new NpgsqlConnection(cs);
con.Open();
con.Execute(@"CREATE OR REPLACE FUNCTION cars_above(min_price int) RETURNS SETOF cars AS $$
SELECT * FROM cars WHERE price > min_price ORDER BY id
$$ LANGUAGE sql");
// PostgreSQL functions are called with SELECT
var cars = con.Query<Car>("SELECT * FROM cars_above(@min)", new { min = 50000 });
Console.WriteLine(string.Join(", ", cars.Select(c => c.Name)));
// for databases with stored procedures: commandType: CommandType.StoredProcedure
record Car(int Id, string Name, int Price);
The program creates a function and calls it.
$ dotnet run Audi, Mercedes, Bentley, Lada 2, Toyota 2
C# Dapper delete row
The following example deletes a row from the table.
using Dapper;
using Npgsql;
string cs = "User ID=postgres;Password=passwd;Host=localhost;Database=testdb";
using var con = new NpgsqlConnection(cs);
con.Open();
int delRows = con.Execute("DELETE FROM cars WHERE id = @id", new { id = 8 });
if (delRows > 0)
{
Console.WriteLine("car deleted");
}
The example deletes the row with the id 8 with the Execute method. The
parameter keeps the id out of the SQL text.
$ dotnet run car deleted
Source
In this article we have shown how to program databases in C# with Dapper.
Author
List all C# tutorials.