首页 > 其他分享 >EF Core 中使用事务

EF Core 中使用事务

时间:2024-03-30 17:13:08浏览次数:17  
标签:Core 事务 transaction EF connection context var new

使用事务
项目
2023/10/05
14 个参与者

反馈
本文内容
默认事务行为
控制事务
保存点
跨上下文事务
使用外部 DbTransactions(仅限关系数据库)
使用 System.Transactions
显示较少选项
事务允许以原子方式处理多个数据库操作。 如果已提交事务,则所有操作都会成功应用到数据库。 如果已回滚事务,则所有操作都不会应用到数据库。

提示

可在 GitHub 上查看此文章的示例。

默认事务行为
默认情况下,如果数据库提供程序支持事务,则会在事务中应用对 SaveChanges 的单一调用中的所有更改。 如果其中有任何更改失败,则会回滚事务且所有更改都不会应用到数据库。 这意味着,SaveChanges 可保证完全成功,或在出现错误时不修改数据库。

对于大多数应用程序,此默认行为已足够。 如果应用程序要求被视为有必要,则应该仅手动控制事务。

控制事务
可以使用 DbContext.Database API 开始、提交和回滚事务。 以下示例显示了在单个事务中执行的两个 SaveChanges 操作以及一个 LINQ 查询:

C#

复制
using var context = new BloggingContext();
using var transaction = context.Database.BeginTransaction();

try
{
context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
context.SaveChanges();

context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/visualstudio" });
context.SaveChanges();

var blogs = context.Blogs
.OrderBy(b => b.Url)
.ToList();

// Commit transaction if all commands succeed, transaction will auto-rollback
// when disposed if either commands fails
transaction.Commit();
}
catch (Exception)
{
// TODO: Handle failure
}
虽然所有关系数据库提供程序都支持事务,但在调用事务 API 时,可能会引发其他提供程序类型或不执行任何操作。

备注

以这种方式手动控制事务的操作与隐式调用的重试执行策略不兼容。 有关详细信息,请参阅连接复原能力。

保存点
如果调用 SaveChanges 且事务已在上下文中进行,则在保存任何数据之前,EF 会自动创建保存点。 保存点是数据库事务中的点,如果发生错误或出于任何其他原因,可能会回滚到这些点。 如果 SaveChanges 遇到任何错误,则会自动将事务回滚到保存点,使事务处于相同状态,就好像从未开始。 这样可以解决问题并重试保存,尤其是在出现乐观并发问题时。

警告

保存点与 SQL Server 的多重活动结果集 (MARS) 不兼容。 当连接上启用了 MARS 时,EF 不会创建保存点,即使 MARS 未处于活动使用状态。 如果在 SaveChanges 过程中出现错误,则该事务可能处于未知状态。

还可以手动管理保存点,就像管理事务一样。 以下示例在事务中创建保存点,并在失败时回滚到该保存点:

C#

复制
using var context = new BloggingContext();
using var transaction = context.Database.BeginTransaction();

try
{
context.Blogs.Add(new Blog { Url = "https://devblogs.microsoft.com/dotnet/" });
context.SaveChanges();

transaction.CreateSavepoint("BeforeMoreBlogs");

context.Blogs.Add(new Blog { Url = "https://devblogs.microsoft.com/visualstudio/" });
context.Blogs.Add(new Blog { Url = "https://devblogs.microsoft.com/aspnet/" });
context.SaveChanges();

transaction.Commit();
}
catch (Exception)
{
// If a failure occurred, we rollback to the savepoint and can continue the transaction
transaction.RollbackToSavepoint("BeforeMoreBlogs");

// TODO: Handle failure, possibly retry inserting blogs
}
跨上下文事务
您还可以跨多个上下文实例共享一个事务。 此功能仅在使用关系数据库提供程序时才可用,因为它需要使用特定于关系数据库的 DbTransaction 和 DbConnection。

若要共享事务,上下文必须共享 DbConnection 和 DbTransaction。

允许在外部提供连接
共享 DbConnection 需要在构造上下文时向其中传入连接的功能。

允许在外部提供 DbConnection 的最简单方式是,停止使用 DbContext.OnConfiguring 方法来配置上下文并在外部创建 DbContextOptions,然后将其传递到上下文构造函数。

提示

DbContextOptionsBuilder 是在 DbContext.OnConfiguring 中用于配置上下文的 API,现在即将在外部使用它来创建 DbContextOptions。

C#

复制
public class BloggingContext : DbContext
{
public BloggingContext(DbContextOptions<BloggingContext> options)
: base(options)
{
}

public DbSet<Blog> Blogs { get; set; }
}
替代方法是继续使用 DbContext.OnConfiguring,但接受已保存并随后在 DbContext.OnConfiguring 中使用的 DbConnection。

C#

复制
public class BloggingContext : DbContext
{
private DbConnection _connection;

public BloggingContext(DbConnection connection)
{
_connection = connection;
}

public DbSet<Blog> Blogs { get; set; }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(_connection);
}
}
共享连接和事务
现在可以创建共享同一连接的多个上下文实例。 然后使用 DbContext.Database.UseTransaction(DbTransaction) API 在同一事务中登记两个上下文。

C#

复制
using var connection = new SqlConnection(connectionString);
var options = new DbContextOptionsBuilder<BloggingContext>()
.UseSqlServer(connection)
.Options;

using var context1 = new BloggingContext(options);
using var transaction = context1.Database.BeginTransaction();
try
{
context1.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
context1.SaveChanges();

using (var context2 = new BloggingContext(options))
{
context2.Database.UseTransaction(transaction.GetDbTransaction());

var blogs = context2.Blogs
.OrderBy(b => b.Url)
.ToList();

context2.Blogs.Add(new Blog { Url = "http://dot.net" });
context2.SaveChanges();
}

// Commit transaction if all commands succeed, transaction will auto-rollback
// when disposed if either commands fails
transaction.Commit();
}
catch (Exception)
{
// TODO: Handle failure
}
使用外部 DbTransactions(仅限关系数据库)
如果使用多个数据访问技术来访问关系数据库,则可能希望在这些不同技术所执行的操作之间共享事务。

以下示例显示了如何在同一事务中执行 ADO.NET SqlClient 操作和 Entity Framework Core 操作。

C#

复制
using var connection = new SqlConnection(connectionString);
connection.Open();

using var transaction = connection.BeginTransaction();
try
{
// Run raw ADO.NET command in the transaction
var command = connection.CreateCommand();
command.Transaction = transaction;
command.CommandText = "DELETE FROM dbo.Blogs";
command.ExecuteNonQuery();

// Run an EF Core command in the transaction
var options = new DbContextOptionsBuilder<BloggingContext>()
.UseSqlServer(connection)
.Options;

using (var context = new BloggingContext(options))
{
context.Database.UseTransaction(transaction);
context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
context.SaveChanges();
}

// Commit transaction if all commands succeed, transaction will auto-rollback
// when disposed if either commands fails
transaction.Commit();
}
catch (Exception)
{
// TODO: Handle failure
}
使用 System.Transactions
如果需要跨较大作用域进行协调,则可以使用环境事务。

C#

复制
using (var scope = new TransactionScope(
TransactionScopeOption.Required,
new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
{
using var connection = new SqlConnection(connectionString);
connection.Open();

try
{
// Run raw ADO.NET command in the transaction
var command = connection.CreateCommand();
command.CommandText = "DELETE FROM dbo.Blogs";
command.ExecuteNonQuery();

// Run an EF Core command in the transaction
var options = new DbContextOptionsBuilder<BloggingContext>()
.UseSqlServer(connection)
.Options;

using (var context = new BloggingContext(options))
{
context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
context.SaveChanges();
}

// Commit transaction if all commands succeed, transaction will auto-rollback
// when disposed if either commands fails
scope.Complete();
}
catch (Exception)
{
// TODO: Handle failure
}
}
还可以在显式事务中登记。

C#

复制
using (var transaction = new CommittableTransaction(
new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
{
var connection = new SqlConnection(connectionString);

try
{
var options = new DbContextOptionsBuilder<BloggingContext>()
.UseSqlServer(connection)
.Options;

using (var context = new BloggingContext(options))
{
context.Database.OpenConnection();
context.Database.EnlistTransaction(transaction);

// Run raw ADO.NET command in the transaction
var command = connection.CreateCommand();
command.CommandText = "DELETE FROM dbo.Blogs";
command.ExecuteNonQuery();

// Run an EF Core command in the transaction
context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
context.SaveChanges();
context.Database.CloseConnection();
}

// Commit transaction if all commands succeed, transaction will auto-rollback
// when disposed if either commands fails
transaction.Commit();
}
catch (Exception)
{
// TODO: Handle failure
}
}
备注

如果使用异步 API,请确保在 TransactionScope 构造函数中指定 TransactionScopeAsyncFlowOption.Enabled ,以确保环境事务跨异步调用流动。

有关 TransactionScope 和环境事务的详细信息,请参阅此文档。

System.Transactions 的限制
EF Core 依赖数据库提供程序以实现对 System.Transactions 的支持。 如果提供程序未实现对 System.Transactions 的支持,则可能会完全忽略对这些 API 的调用。 SqlClient 支持它。

重要

建议你测试在依赖提供程序以管理事务之前 API 与该提供程序的行为是否正确。 如果不正确,则建议你与数据库提供程序的维护人员联系。

System.Transactions 中的分布式事务支持仅添加到了 Windows 版 .NET 7.0。 尝试在较旧的 .NET 版本或非 Windows 平台上使用分布式事务将失败。

TransactionScope 不支持异步提交/回滚;这意味着同步处置它会阻塞正在执行的线程,直到该操作完成。

标签:Core,事务,transaction,EF,connection,context,var,new
From: https://www.cnblogs.com/zy8899/p/18105743

相关文章

  • EFCore中ExecuteUpdate 和 ExecuteDelete
    ExecuteUpdate和ExecuteDelete项目2023/05/114个参与者反馈本文内容ExecuteDeleteExecuteUpdateChangetracking事务显示另外3个备注EFCore7.0中已引入此功能。ExecuteUpdate和ExecuteDelete是一种将数据保存到数据库的方法,无需使用EF的传统更改跟踪和SaveChang......
  • NET Core使用Grpc通信(一):一元请求
    gRPC是一个现代的开源高性能远程过程调用(RPC)框架,它可以高效地连接数据中心内和跨数据中心的服务,支持负载平衡、跟踪、运行状况检查和身份验证。gRPC通过使用ProtocolBuffers作为数据传输格式,实现了在不同平台上的通信,并支持双向流和流式传输。RPC是远程过程调用的缩写,实现......
  • 题解 CF70E【Information Reform】
    题解CF70E【InformationReform】题目描述\(n\)个点的树,边权为\(1\)。可以花费常数\(k\),在一个点上建基站。每个点\(i\)需要找到离他最近的基站\(a_i\),花费\(d[dis(i,a_i)]\)。一种方案的总花费是建基站的花费加上每个点的花费之和。最小化总花费。输出方案\(a_i\)。......
  • 初识分布式事务
    本地事务本地事务,也就是传统的单机事务。在传统数据库事务中,必须要满足四个原则:分布式事务分布式事务,就是指不是在单个服务或单个数据库架构下,产生的事务,例如:跨数据源的分布式事务跨服务的分布式事务综合情况在数据库水平拆分、服务垂直拆分之后,一个业务操作通常要跨多......
  • ON1 Effects 2023:打造梦幻影像世界的创意工具 mac版
    在数字摄影日益盛行的今天,ON1Effects2023以其强大的功能和卓越的性能,为摄影后期处理带来了全新的体验。这款软件集多种先进工具于一身,不仅可以帮助摄影师高效地处理海量图片,还能激发他们无限的创意灵感。→→↓↓载ON1Effects2023macON1Effects2023拥有极为丰富的特效库......
  • MySQL 事务的两阶段提交--转
    什么是事务事务是数据库中一组原子性的操作,要么全部成功,要么全部失败。事务具有四个特性,即原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability),简称ACID。在MySQL中,我们可以使用 begin 或 starttransaction 命令开启一个事务,使用 commit 命令提交......
  • springBoot AOP 深入原理,及 @Before,@Around,@After,@AfterReturn,@AfterThrowing执行
    连接点(Joinpoint):程序能够应用通知的一个“时机”,这些“时机”就是连接点,例如方法被调用时、异常被抛出时等等。——可以理解为被aop拦截的类或者方法就是连接点。通知(Advice):通知定义了切面是什么以及何时使用。描述了切面要完成的工作和何时需要执行这个工作。——可以理解为被......
  • MySQL的InnoDB引擎的事务原理以及MVCC
    目录一、事务原理二、redolog三、undolog四、MVCC    1.基础概念    2.隐藏字段    3.undolog        4.readview        5.原理分析一、事务原理        1).事务        事务是一组操作的集合,它......
  • 谈谈分布式事务TCC
    TCC(Try-Confirm-Cancel)是一种分布式事务处理模型,用于解决在分布式系统中执行跨服务或跨资源的事务时的一致性问题。与传统的两阶段提交(2PC)和三阶段提交(3PC)相比,TCC提供了一种更为灵活和适应性更强的解决方案,尤其适用于长事务处理和需要高度一致性保证的业务场景。###工作原理......
  • NVIDIA一直宣传的DPU是个啥东西,啥用处? —— NVIDIA BlueField-3 DPU
    地址:https://www.bilibili.com/video/BV1ys4y1z7nS/?spm_id_from=333.788.recommend_more_video.3&vd_source=f1d0f27367a99104c397918f0cf362b7无意间看到了一个比较靠谱的解释:(来自地址:https://www.bilibili.com/video/BV1ys4y1z7nS/)......