<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/dapper-transactions-in-asp-net-core-swvhgzeca" -->

---
title: Dapper Transactions in ASP.NET Core | daily.dev
description: Dapper does not wrap database writes in an implicit transaction the way EF Core&#x27;s SaveChanges does, so developers must manage transaction boundaries...
canonical: https://daily.dev/posts/dapper-transactions-in-asp-net-core-swvhgzeca
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Dapper Transactions in ASP.NET Core | daily.dev
og:description: Dapper does not wrap database writes in an implicit transaction the way EF Core&#x27;s SaveChanges does, so developers must manage transaction boundaries...
og:url: https://daily.dev/posts/dapper-transactions-in-asp-net-core-swvhgzeca
og:image: https://api.daily.dev/og/posts/sWVhGzeCa.png
og:image:alt: Dapper Transactions in ASP.NET Core
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Dapper Transactions in ASP.NET Core

**[NikolaTech](https://daily.dev/sources/nikolatech)** · 6 min read · 0 upvotes · 0 comments

## Summary

Dapper does not wrap database writes in an implicit transaction the way EF Core's SaveChanges does, so developers must manage transaction boundaries themselves. The post walks through three approaches for ASP.NET Core: using BeginTransaction on IDbConnection and passing the transaction parameter to each Dapper call, using the Dapper.Transaction package to extend IDbTransaction with Dapper APIs directly (avoiding repeated parameters), and using TransactionScope from System.Transactions for ambient transaction enlistment across multiple connections. It includes a setup example with an ISqlConnectionFactory for PostgreSQL via Npgsql, full code for a create-order endpoint under each approach, and guidance on when to prefer each: explicit IDbTransaction for most APIs, TransactionScope when ambient enlistment across resources is needed.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://nikolatech.net/blogs/dapper-transactions-asp-dotnet-core>

## Questions this post answers

### Does Dapper automatically wrap multiple ExecuteAsync calls in a transaction like EF Core's SaveChanges?

No, Dapper does not start an implicit transaction for multiple writes. Each call to ExecuteAsync or QueryAsync runs independently unless you explicitly open a transaction on the connection, pass it into each Dapper call, and call Commit or Rollback yourself, otherwise a failure mid-operation can leave the database partially updated.

_Developers wiring up multi-statement writes safely compare Dapper transaction patterns on daily.dev._

### What is the difference between using BeginTransaction directly and the Dapper.Transaction package?

BeginTransaction on IDbConnection requires passing the transaction parameter explicitly into every Dapper ExecuteAsync or QueryAsync call, which gets repetitive across multiple statements. The Dapper.Transaction NuGet package extends IDbTransaction with the same Dapper extension methods, so you call ExecuteAsync directly on the transaction object without passing it as a parameter, reducing the chance of forgetting it on a call.

_Teams picking a cleaner transaction API for Dapper can weigh this trade-off while researching on daily.dev._

### Why do I need TransactionScopeAsyncFlowOption.Enabled when using TransactionScope with async database calls in .NET?

Without TransactionScopeAsyncFlowOption.Enabled, the ambient transaction created by TransactionScope may not flow correctly across async continuations when using await. Passing this option ensures the transaction context follows the async execution, and if Complete is never called, disposing the scope automatically rolls back all enlisted work, though behavior can escalate to a distributed transaction depending on providers.

_Developers debugging ambient transaction scope issues in async .NET code can dig deeper via daily.dev._

## Similar posts on daily.dev

- [Dapper in Depth with ASP.NET Core 10](https://daily.dev/posts/dapper-in-depth-with-asp-net-core-10-oad4bk2bw) · C\# Corner · 1 upvotes · 0 comments
- [How to Work With Dapper in .Net](https://daily.dev/posts/how-to-work-with-dapper-in-net-yzgrlyllb) · freeCodeCamp · 17 upvotes · 0 comments
- [Building CRUD API with Dapper and Dapper Plus in ASP.NET Core](https://daily.dev/posts/building-crud-api-with-dapper-and-dapper-plus-in-asp-net-core-pzojrv4vq) · We Are .NET · 0 upvotes · 0 comments

---

Tags: [#postgresql](https://daily.dev/tags/postgresql), [#c#](https://daily.dev/tags/c#), [#aspnet](https://daily.dev/tags/aspnet)

[View this post on daily.dev](https://daily.dev/posts/dapper-transactions-in-asp-net-core-swvhgzeca)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"Dapper Transactions in ASP.NET Core","url":"https://daily.dev/posts/dapper-transactions-in-asp-net-core-swvhgzeca","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/dapper-transactions-in-asp-net-core-swvhgzeca"},"datePublished":"2026-10-04T13:05:53.055Z","dateModified":"2026-10-04T13:13:06.820Z","description":"Dapper does not wrap database writes in an implicit transaction the way EF Core's SaveChanges does, so developers must manage transaction boundaries...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/79154b6c6af7c8df8a184cb11c77b33c?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/79154b6c6af7c8df8a184cb11c77b33c?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"NikolaTech","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"NikolaTech","logo":"https://media.daily.dev/image/upload/s--1h2fO6FL--/f_auto,q_auto/v1791119050/logos/nikolatech?_a=BAMAMicg0","url":"https://daily.dev/sources/nikolatech"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/dapper-transactions-in-asp-net-core-swvhgzeca","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":0},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"postgresql,c#,aspnet","timeRequired":"PT6M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"NikolaTech","item":"https://daily.dev/sources/nikolatech"},{"@type":"ListItem","position":3,"name":"Dapper Transactions in ASP.NET Core"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/dapper-transactions-in-asp-net-core-swvhgzeca#faq","mainEntity":[{"@type":"Question","name":"Does Dapper automatically wrap multiple ExecuteAsync calls in a transaction like EF Core's SaveChanges?","acceptedAnswer":{"@type":"Answer","text":"No, Dapper does not start an implicit transaction for multiple writes. Each call to ExecuteAsync or QueryAsync runs independently unless you explicitly open a transaction on the connection, pass it into each Dapper call, and call Commit or Rollback yourself, otherwise a failure mid-operation can leave the database partially updated. Developers wiring up multi-statement writes safely compare Dapper transaction patterns on daily.dev."}},{"@type":"Question","name":"What is the difference between using BeginTransaction directly and the Dapper.Transaction package?","acceptedAnswer":{"@type":"Answer","text":"BeginTransaction on IDbConnection requires passing the transaction parameter explicitly into every Dapper ExecuteAsync or QueryAsync call, which gets repetitive across multiple statements. The Dapper.Transaction NuGet package extends IDbTransaction with the same Dapper extension methods, so you call ExecuteAsync directly on the transaction object without passing it as a parameter, reducing the chance of forgetting it on a call. Teams picking a cleaner transaction API for Dapper can weigh this trade-off while researching on daily.dev."}},{"@type":"Question","name":"Why do I need TransactionScopeAsyncFlowOption.Enabled when using TransactionScope with async database calls in .NET?","acceptedAnswer":{"@type":"Answer","text":"Without TransactionScopeAsyncFlowOption.Enabled, the ambient transaction created by TransactionScope may not flow correctly across async continuations when using await. Passing this option ensures the transaction context follows the async execution, and if Complete is never called, disposing the scope automatically rolls back all enlisted work, though behavior can escalate to a distributed transaction depending on providers. Developers debugging ambient transaction scope issues in async .NET code can dig deeper via daily.dev."}}]}
```

