EFCore 5 中的 Savepoints
Intro
EFCore 5中引入了一個新特性,叫做 Savepoints,主要是事務中使用,個人感覺有點類似于 Windows 上的系統還原點,如果事務發生了例外,可以回滾到某一個還原點,
Savepoints
當我們在一個事務里執行 SaveChanges 的時候,EF Core 會在保存資料之前自動的創建一個 savepoint,Savepoints 有點類似于系統還原點的概念,我們可以回滾到指定的 savepoint,
當事務發生錯誤的時候,會自動回滾到事務創建的 savepoint 回滾到事務開始之前的狀態,以便于我們做重試或可能的修復錯誤或其他邏輯,
我們可以通過 CreateSavepoint 來手動創建一個 savepoint,使用 RollbackToSavepoint 來回滾到某一個 savepoint
來看一個微軟的示例代碼吧:
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
}
Sample
我們自己來動手一試,示例代碼如下:
var services = new ServiceCollection();
services.AddDbContext<TestDbContext>(options =>
{
options.UseSqlite("Data Source=Application.db;Cache=Shared")
.LogTo(Console.WriteLine, LogLevel.Warning)
;
});
using var provider = services.BuildServiceProvider();
using var scope = provider.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<TestDbContext>();
dbContext.Database.EnsureCreated();
Console.WriteLine($"Posts count:{dbContext.Posts.Count()}");
using var transaction = dbContext.Database.BeginTransaction();
try
{
dbContext.Posts.Add(new Post() { Author = "Tom", Title = "Date changed", PostedAt = DateTime.UtcNow, });
dbContext.Posts.Add(new Post() { Author = "Tom", Title = "Date changed", PostedAt = DateTime.UtcNow, });
dbContext.SaveChanges();
transaction.CreateSavepoint("Stage1");
Console.WriteLine($"Posts count:{dbContext.Posts.Count()}");
dbContext.Posts.Add(new Post() { Author = "Alice", Title = "Test", PostedAt = DateTime.UtcNow, });
dbContext.SaveChanges();
transaction.CreateSavepoint("Stage2");
Console.WriteLine($"Posts count:{dbContext.Posts.Count()}");
throw new InvalidOperationException();
transaction.Commit();
}
catch (Exception)
{
Console.WriteLine("Exception throw");
transaction.RollbackToSavepoint("Stage1");
}
Console.WriteLine($"Posts count:{dbContext.Posts.Count()}");
示例代碼中創建了兩個 savepoint,然后拋出了一個例外,捕獲例外后回滾到第一個 savepoint
輸出結果如下:

可以看到,只有第一個 savepoint 之前的資料保存了下來,第二個 savepoint 雖然資料成功保存了,但是又被回滾了,最終只有第一個 savepoint 之前的資料變更被保存了下來
More
通過 savepoint 我們就可以使得事務控制更加精細,可以更能夠好的控制事務中的資料變更
但是需要注意的是,這個功能不要和 Sql Server 中的 Multiple Active Result Sets 一起使用,一旦發生了錯誤,事務控制可能會發生不可預期的情況,
Savepoints are incompatible with SQL Server's Multiple Active Result Sets, and are not used. If an error occurs during
SaveChanges, the transaction may be left in an unknown state.
References
- https://docs.microsoft.com/en-us/ef/core/saving/transactions#savepoints
- https://github.com/WeihanLi/SamplesInPractice/blob/master/EF5Samples/SavePointsTest.cs
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/251305.html
標籤:.NET技术
上一篇:NVelocity實作代碼生成
