EFCore 5 新特性 —— Savepoints

EFCore 5 中的 Savepoints

Intro

EFCore 5中引入了一個新特性,叫作 Savepoints,主要是事務中使用,我的感受有點相似於 Windows 上的系統還原點,若是事務發生了異常,能夠回滾到某一個還原點。git

Savepoints

當咱們在一個事務裏執行 SaveChanges 的時候,EF Core 會在保存數據以前自動的建立一個 savepointSavepoints 有點相似於系統還原點的概念,咱們能夠回滾到指定的 savepoint,github

當事務發生錯誤的時候,會自動回滾到事務建立的 savepoint 回滾到事務開始以前的狀態,以便於咱們作重試或可能的修復錯誤或其餘邏輯。ide

咱們能夠經過 CreateSavepoint 來手動建立一個 savepoint,使用 RollbackToSavepoint 來回滾到某一個 savepointui

來看一個微軟的示例代碼吧:code

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

咱們本身來動手一試,示例代碼以下:blog

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事務

輸出結果以下:ip

output

能夠看到,只有第一個 savepoint 以前的數據保存了下來,第二個 savepoint 雖然數據成功保存了,可是又被回滾了,最終只有第一個 savepoint 以前的數據變動被保存了下來get

More

經過 savepoint 咱們就可使得事務控制更加精細,能夠更可以好的控制事務中的數據變動it

可是須要注意的是,這個功能不要和 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

相關文章
相關標籤/搜索