DotNetCore跨平台~EFCore废弃了TransactionScope取而代之的Context.Database.BeginTransaction

TransactionScope是.net平台基于的分布式事务组件,它默认为本地事务,同时当系统有需要时可以自动提升为分布式事务,而对系统的前提是要开启MSDTC服务,必要时需要在数据库服务器与应用服务器之间添加hosts的映射,这些在之前已经写过很多文章了,在这里不再说了。

之前对TransactionScope的一些理解和总结

第二十六回   将不确定变为确定~transactionscope何时提升为分布式事务?

第二十七回   将不确定变为确定~transactionscope何时提升为分布式事务~续

第二十八回   将不确定变为确定~transactionscope何时提升为分布式事务~再续(避免引起不必要的MSDTC)

第三十七回   将不确定变为确定~transactionscope何时提升为分布式事务~SQL2005与SQL2008不同

第三十八回   将不确定变为确定~transactionscope何时提升为分布式事务?(sql2005数据库解决提升到MSDTC的办法)

在efcore平台时,你使用TransactionScope将会出现异常,微软会提示你去查看相关资料,这回资料挺准!https://docs.microsoft.com/en-us/ef/core/saving/transactions

本文章主要说了几点内容

  1. 默认的事务-savechanges依旧是一个事务
  2. 单个上下文实现事务
  3. 不同上下文之间实现事务

一 savechanges依旧是一个事务

和之前的ef一样,在进行saveChanges()操作时,本身就是一个事务块,而大叔仓储习惯把每个操作curd都有自己的saveChanges里,而把数据上下文的savechanges对外隐藏,所以如果你要对两个仓储进行insert操作时,你需要添加一个外层的事务来保证数据一致性,这时微软给出了解决方案。

二 单个上下文实现事务

对于一个数据上下文来说,如果你是多个savechanges,那么可以使用context.Database.BeginTransaction()来实现事务。

  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
                }
            }
        }

三 不同上下文之间实现事务

对于前面的TransactionScope来说,如果是不同的数据上下文来说,我们是无法实现事务操作的,有些同学可以能说它应该被提升为分布式的,但对于EF来说,它是不同实现的,但进行efcore时代之后,这个问题得到了解决!

Cross-context transaction (relational databases only)

You can also share a transaction across multiple context instances. This functionality is only available when using a relational database provider because it requires the use of DbTransaction and DbConnection, 
which are specific to relational databases.

上面说明,可以实现一个跨数据上下文的事务,只关系型数据库支持!这个功能大叔认为非常必要,但看它下面给出的实例是针对一个数据上下文的,并不多个上下文的交

叉事务,即并不是两个数据库之间的事务。

       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();
                    }

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

而如果真正使用多个上下文进行事务的话,同样会出现问题:

           var options = new DbContextOptionsBuilder<DemoContext>()
                          .UseMySql("Server=localhost;DataBase=test2;UID=root;Password=root;charset=utf8;port=3306;SslMode=None")
                          .Options;
            using (var context = new DemoContext(options))
            {
                using (var transaction = context.Database.BeginTransaction())
                {
                    var user = new UserInfo
                    {
                        AddTime = DateTime.Now,
                        Email = "test@sina.com",
                        UserName = "test"
                    };
                    context.UserInfo.Add(user);
                    context.SaveChanges();
                    using (var context2 = new TaxContext())
                    {
                        context2.Database.UseTransaction(transaction.GetDbTransaction());
                        context2.UserInfo.Add(new UserInfo { AddTime = DateTime.Now, Email = "tax_test", UserName = "tax" });
                        context2.SaveChanges();
                    }
                    transaction.Commit();
                }
            }

出现下面异常:告诉你,你的数据库连接不是当前的连接

System.InvalidOperationException:“The specified transaction is not associated with the current connection.
Only transactions associated with the current connection may be used.”

不知道什么时候EF可以解决多数据库事务的问题,当前你可以使用最终一致性的分布式事务来做这事,不过我们还是一起期待中微软为我们提出更简单的解决方案,一个事务

是否为分布式的,应该看数据库所在服务器是否相同,而不是数据库连接串是否一致!

感谢微软这么完整的解释!

本文转自博客园张占岭(仓储大叔)的博客,原文链接:DotNetCore跨平台~EFCore废弃了TransactionScope取而代之的Context.Database.BeginTransaction,如需转载请自行联系原博主。

时间: 2024-10-24 12:21:13

DotNetCore跨平台~EFCore废弃了TransactionScope取而代之的Context.Database.BeginTransaction的相关文章

DotNetCore跨平台~EFCore数据上下文的创建方式

对于DotNetCore来说,把大部分组件者放在DI容器里,在startup中进行注入,在类的构造方法中进行使用,如果某些情况下,无法使用这种DI的方式,也可以自己控制数据上下文的生产过程,下面说一下. 一 标准注入+构造方法使用 数据上下文的定义,带参数的构造方法,注意他和使用什么类型的数据库没有关系,只是单纯的上下文 public partial class ErpContext : DbContext, IERPContext { public ErpContext(DbContextOp

DotNetCore跨平台~EFCore连接Mysql的方式

在.net frameworks的ef里连接mysql我们已经测试通过了,而在dotnet core里的efCore上去连接mysql我们需要测试一下,并且在测试过程中出现了一些问题,当然最后也是解决了,下面总结一下,分享给大家! mysql项目的依赖包 数据上下文和连接串 数据仓储 添加模块扩展 业务层注入 业务实现 mysql项目的依赖包 Microsoft.EntityFrameworkCore MySql.Data.EntityFrameworkCore 数据上下文和连接串 对于mysq

DotNetCore跨平台~文章索引~永久更新

本索引目录主要包括仓储大叔对dotnet core架构的研究与知识积累,从2016年开始进行撰写,到今天已经有一年多了,其中有一些小知识,小技巧,小应用,希望给大家在开发时一些启发,也希望dotnet core越来越好,希望2.0正式版快点出来! 我们的框架应该是基于组件化的! 我们的系统应该是基于微服务化的! 我们的部署,应该是基于自动化的! DotNetCore跨平台目录 DotNetCore跨平台~Startup类的介绍(2016-05-31 16:25) Linux~centos上安装.

DotNetCore跨平台~xUnit和测试报告

在进入dotnet core时代之后,测试驱动开发TDD的主要工具不再是微软的nunit,取而代之的是更通用的xunit,微软把它集成到了dotnetcore的项目里,在安装完成vs2017之后,你可以很容易的建立一个xunit单元测试项目,并且我们的dotnet宿主命令也有对它的支持,当然你在进行CI持续集成时可以使用jenkins里的xunit插件,不过对于原生的工具dotnet test来说,有时候感觉更让人喜欢! 从一个很简单的断言开始 [Fact] public void Test2(

DotNetCore跨平台~服务总线_事件总线的重新设计

理论闲话 之前在.netFramework平台用的好好的,可升级到.net core平台之后,由于不再需要二进制序列化,导致咱们的事件机制遇到了问题,之前大叔的事件一直是将处理程序序列化后进行存储的,处理存储的参数为事件源,一个事件源可以由多个处理程序订阅,当事件源被发布时,这些被序列化的代码段会被回调执行,这是大叔之前的思路,在RedisBus和MemoryBus里已经得到了实现,读过大叔源代码的同学应该有所了解了. 事件源和处理程序 /// <summary> /// 事件源 /// &l

DotNetCore跨平台~问题~NETCoreAPP, Version=v1.0&#039; compatible with one of the target runtimes: &#039;win10-x64

新建console项目之后,编译程序出现以下错误: Can not find runtime target for framework '.NETCoreAPP, Version=v1.0' compatible with one of the target runtimes: 'win10-x64, win81-x64, win8-x64, win7-x64'. Possible causes:        The project has not been restored or resto

DotNetCore跨平台~Dapper的使用

Dapper作为小型ORM的代表作品被我们应用到了dotnet core的项目中,下面将把自己在项目中使用dapper进行curd操作的过程写一下,后期可能会遇到一些问题,大叔也会在这个系列之中进行完善,希望对各位学生有所帮助! 一 安装nuget的dapper包包 二 在startup中注册dapper仓储,并现时注册数据库类型和数据库连接串,因为在mysql和sqlserver中,它们的连接串是不同的,模块化设计请看大叔这篇文章 <DotNetCore跨平台~组件化时代来了> servic

DotNetCore跨平台~组件化时代来了

进行dotnetcore之后,各种对象都是基于DI进行生产的,这就有了对象的生命周期一说,早在autofac里也有相关知识点,这与Microsoft.Extensions.DependencyInjection是完全温和的,方便大家理解,在讲今天的组件化之前,先对DI的三种生命周期进行理解一下: AddSingleton:单例,进程内它是唯一的 AddTransient:瞬息,在对象在当前环境内,作用域内是唯一的 AddScoped:请求,对象在一个HTTP请求内是唯一的 下面来看今天的组件化的

DotNetCore跨平台~聊聊中间件

在进行.net core平台之后,我们如果希望在请求过程中添加一些事件是非常容易的,你可以把这些事件做成一个中间件Middleware,然后这些中间件就会以Http pipeline的管道方式进行相应,并且它们就像是一个职责链,从你定义的第一个中间件开始,一个一个向下传递,直到最后一个中间件完成为止! 前几天我写了在.net core里实现模块化服务,DotNetCore跨平台~组件化时代来了 主要是将我们定义的组件添加到IServiceCollection集合里,然后在程序启动后去注册它们,而