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

相关文章
相关标签/搜索