• EFCore 5 新特性 —— Savepoints


    EFCore 5 中的 Savepoints

    Intro

    EFCore 5中引入了一个新特性,叫做 Savepoints,主要是事务中使用,个人感觉有点类似于 Windows 上的系统还原点,如果事务发生了异常,可以回滚到某一个还原点。

    Savepoints

    当我们在一个事务里执行 SaveChanges 的时候,EF Core 会在保存数据之前自动的创建一个 savepointSavepoints 有点类似于系统还原点的概念,我们可以回滚到指定的 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

    输出结果如下:

    output

    可以看到,只有第一个 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

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    IDEA常用快捷键
    mybatis动态sql总结
    端口被占用的问题解决 Web server failed to start. Port ×× was already in use
    java常见的面试题(二)
    java常见的面试题(一)
    Zookeeper学习总结
    Oracle 创建表空间及用户授权、dmp数据导入、表空间、用户删除
    Navicat远程连接服务器mysql
    HashMap的实现原理?如何保证HashMap线程安全?
    ArrayList和LinkedList内部是怎么实现的?他们之间的区别和优缺点?
  • 原文地址:https://www.cnblogs.com/weihanli/p/14320743.html
Copyright © 2020-2023  润新知