FreeSql接入CAP的实践

CAP

CAP 是一个基于 .NET Standard 的 C# 库,它是一种处理分布式事务的解决方案,同样具有 EventBus 的功能,它具有轻量级、易使用、高性能等特点。

  • https://github.com/dotnetcore/CAP

ADO.NET事务

1.DotNetCore.CAP.MySql中引用 了如下类库.在Commit事务时,会调用 Flush方法推送消息

<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.7" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="3.1.7" />
<PackageReference Include="MySqlConnector" Version="1.0.1" />
  • https://github.com/dotnetcore/CAP/blob/master/src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs

    public class MySqlCapTransaction : CapTransactionBase{public MySqlCapTransaction(IDispatcher dispatcher) : base(dispatcher){}public override void Commit(){Debug.Assert(DbTransaction != null);switch (DbTransaction){case IDbTransaction dbTransaction:dbTransaction.Commit();break;case IDbContextTransaction dbContextTransaction:dbContextTransaction.Commit();break;}Flush();}}

其中我们能看到,事务的提交,会调用父类CapTransactionBase中的方法Flush。他是protected类型的,并未开放出此接口。

       protected virtual void Flush(){while (!_bufferList.IsEmpty){_bufferList.TryDequeue(out var message);_dispatcher.EnqueueToPublish(message);}}

我们来看一下集成 的demo调用

    [Route("~/adonet/transaction")]public IActionResult AdonetWithTransaction(){using (var connection = new MySqlConnection(AppDbContext.ConnectionString)){using (var transaction = connection.BeginTransaction(_capBus, true)){//your business codeconnection.Execute("insert into test(name) values('test')", transaction: (IDbTransaction)transaction.DbTransaction);//for (int i = 0; i < 5; i++)//{_capBus.Publish("sample.rabbitmq.mysql", DateTime.Now);//}}}return Ok();}
  • https://github.com/dotnetcore/CAP/blob/master/src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs

代码中通过扩展IDbConnection类,增加BeginTransaction方法,传递了注入的_capBus类,传了autoCommit

private readonly ICapPublisher _capBus;public PublishController(ICapPublisher capPublisher)
{_capBus = capPublisher;
}
/// <summary>
/// Start the CAP transaction
/// </summary>
/// <param name="dbConnection">The <see cref="IDbConnection" />.</param>
/// <param name="publisher">The <see cref="ICapPublisher" />.</param>
/// <param name="autoCommit">Whether the transaction is automatically committed when the message is published</param>
/// <returns>The <see cref="ICapTransaction" /> object.</returns>
public static ICapTransaction BeginTransaction(this IDbConnection dbConnection,ICapPublisher publisher, bool autoCommit = false)
{if (dbConnection.State == ConnectionState.Closed){dbConnection.Open();}var dbTransaction = dbConnection.BeginTransaction();publisher.Transaction.Value = publisher.ServiceProvider.GetService<ICapTransaction>();return publisher.Transaction.Value.Begin(dbTransaction, autoCommit);
}

autoCommit:false,(此属性会自动提交事务,集成其他ORM,不建议开启)因为,我们只要调用 了Publish,他会调用MySqlCapTransaction中的Commit(),并执行Flush,即消息 会发出去。IDbContextTransaction

这段代码是非常 重要的。

    publisher.Transaction.Value = publisher.ServiceProvider.GetService<ICapTransaction>();

从CapPublisher中可以看出,事务是通过AsyncLocal实现状态共享的。

internal class CapPublisher : ICapPublisher
{public AsyncLocal<ICapTransaction> Transaction { get; }
}

publisher.Transaction.Value的类型实现上才是ICapTransaction ,

CapTransactionExtensions.cs还有一个扩展方法,调用Begin,相当于给当前控制器上注入的ICapPublisher设置了new MySqlConnection(AppDbContext.ConnectionString).BeginTransaction()的值。

      public static ICapTransaction Begin(this ICapTransaction transaction,IDbTransaction dbTransaction, bool autoCommit = false){transaction.DbTransaction = dbTransaction;transaction.AutoCommit = autoCommit;return transaction;}

对于ADO.NET,我们只要传递transaction,就能保证发送消息和操作DB是一个事务了。。

EF Core事务

同样,我们看扩展方法和使用方式

    public static IDbContextTransaction BeginTransaction(this DatabaseFacade database,ICapPublisher publisher, bool autoCommit = false){var trans = database.BeginTransaction();publisher.Transaction.Value = publisher.ServiceProvider.GetService<ICapTransaction>();var capTrans = publisher.Transaction.Value.Begin(trans, autoCommit);return new CapEFDbTransaction(capTrans);}

dbContext.Database就是DatabaseFacade类型。直接能BeginTransaction事务。

[Route("~/ef/transaction")]
public IActionResult EntityFrameworkWithTransaction([FromServices]AppDbContext dbContext)
{using (var trans = dbContext.Database.BeginTransaction(_capBus, autoCommit: false)){dbContext.Persons.Add(new Person() { Name = "ef.transaction" });for (int i = 0; i < 1; i++){_capBus.Publish("sample.rabbitmq.mysql", DateTime.Now);}dbContext.SaveChanges();trans.Commit();}return Ok();
}

同样,还有一个Begin扩展方法,仅仅是给ICapTransaction赋下值。

public static ICapTransaction Begin(this ICapTransaction transaction,IDbContextTransaction dbTransaction, bool autoCommit = false)
{transaction.DbTransaction = dbTransaction;transaction.AutoCommit = autoCommit;return transaction;
}

在这个demo,上,autoCommit是false,因为dbContext有自己的SaveChanges(),如果发送不太合适。SaveChanges()要做好些操作,具体不太情况是什么。具体不详细研究。

但我们可以看下CapTransactionBase源码,DbTransaction是Object类型。

EF Core中的事务类型是IDbContextTransaction

ADO.NET实际是IDbTransaction类型。

 public object DbTransaction { get; set; }

所以在最开始的那段代码,判断DbTransaction,是哪种类型,然后调用自身内部使用的事务进行Commit()。如果要集成其他ORM,但又想去掉EFCore的依赖,然后增加其他ORM,如下类似的处理,就是关键,比如CommitAsync,Commit,Roolback()

    public override void Commit(){Debug.Assert(DbTransaction != null);switch (DbTransaction){case IDbTransaction dbTransaction:dbTransaction.Commit();break;case IDbContextTransaction dbContextTransaction:dbContextTransaction.Commit();break;}Flush();}

还有MySqlDataStorage.cs

  • https://github.com/dotnetcore/CAP/blob/master/src/DotNetCore.CAP.MySql/IDataStorage.MySql.cs

判断dbTransaction的类型,然后获取当前事务,引用其他ORM,记得修改此处。

    var dbTrans = dbTransaction as IDbTransaction;if (dbTrans == null && dbTransaction is IDbContextTransaction dbContextTrans){dbTrans = dbContextTrans.GetDbTransaction();}

参考项目(不打算维护)

  • https://github.com/luoyunchong/DotNetCore.CAP.Provider

  • 维护就要保证与上层Dotnetcore/cap项目保持同步,这是一件困难的事。

  • 还有一个重要的原因是:我们有更简单的方式。

FreeSql接入CAP(最简单的方式)

关于此问题的想法

  • https://github.com/luoyunchong/DotNetCore.CAP.Provider/issues/1

我们还是引用各自官方的库

Install-Package DotNetCore.CAP.Dashboard
Install-Package DotNetCore.CAP.MySql
Install-Package DotNetCore.CAP.RabbitMQ
Install-Package FreeSql
Install-Package FreeSql.DbContext
Install-Package FreeSql.Provider.MySqlConnector

关于CAP集成的方式,配置项,这里不做详情,官方地址有中文:http://cap.dotnetcore.xyz/

  • 代码参考。因为只有一个类,我们自行复制项目即可。

  • https://github.com/luoyunchong/lin-cms-dotnetcore/blob/master/src/LinCms.Application/CapUnitOfWorkExtensions.cs

重写扩展方法,BeginTransaction。是基于IUnitOfWork的扩展。

提交事务调用Commit(IUnitOfWork)时,内部再通过反射调用 ICapTransaction中protected类型的方法Flush。

  public static class CapUnitOfWorkExtensions{public static void Flush(this ICapTransaction capTransaction){capTransaction?.GetType().GetMethod("Flush", BindingFlags.Instance | BindingFlags.NonPublic)?.Invoke(capTransaction, null);}public static ICapTransaction BeginTransaction(this IUnitOfWork unitOfWork, ICapPublisher publisher, bool autoCommit = false){publisher.Transaction.Value = (ICapTransaction)publisher.ServiceProvider.GetService(typeof(ICapTransaction));return publisher.Transaction.Value.Begin(unitOfWork.GetOrBeginTransaction(), autoCommit);}public static void Commit(this ICapTransaction capTransaction, IUnitOfWork unitOfWork){unitOfWork.Commit();capTransaction.Flush();}}

注入我们的FreeSql

public void ConfigureServices(IServiceCollection services){IConfigurationSection configurationSection = Configuration.GetSection($"ConnectionStrings:MySql");IFreeSql fsql = new FreeSqlBuilder().UseConnectionString(DataType.MySql, configurationSection.Value);.UseNameConvert(NameConvertType.PascalCaseToUnderscoreWithLower).UseAutoSyncStructure(true).UseNoneCommandParameter(true).UseMonitorCommand(cmd =>{Trace.WriteLine(cmd.CommandText + ";");}).Build();services.AddSingleton(fsql);services.AddFreeRepository();services.AddScoped<UnitOfWorkManager>();
}

示例

    [HttpGet("~/freesql/unitofwork/{id}")]public DateTime UnitOfWorkManagerTransaction(int id, [FromServices] IBaseRepository<Book> repo){DateTime now = DateTime.Now;using (IUnitOfWork uow = _unitOfWorkManager.Begin()){ICapTransaction trans = _unitOfWorkManager.Current.BeginTransaction(_capBus, false);repo.Insert(new Book(){Author = "luoyunchong",Summary = "2",Title = "122"});_capBus.Publish("freesql.time", now);trans.Commit(uow);}return now;}[NonAction][CapSubscribe("freesql.time")]public void GetTime(DateTime time){Console.WriteLine($"time:{time}");}

注意trans不需要using,freesql内部会释放资源。,也可using,但请更新到最新的freesql版本。

ICapTransaction trans = _unitOfWorkManager.Current.BeginTransaction(_capBus, false);

提交事务,也请调用扩展方法,否则事务无法正常。

trans.Commit(uow);

源码位置

  • https://github.com/luoyunchong/lin-cms-dotnetcore/blob/master/src/LinCms.Web/Controllers/v1/TestController.cs

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/306890.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

windows和linux命令行一样吗,微软改进Windows命令行 目的是和Linux命令行相抗衡

微软官方人员承认微软正在对Windows命令行环境或是Windows终端进行全方位的改进&#xff0c;目的是能和Linux命令行相竞争。随着Linux云服务器的大量被采用&#xff0c;很多Linux云服务器都没有安装图形界面&#xff0c;只使用终端能够大幅度提高运行速度&#xff0c;微软想通过…

vue 离开页面事件_【必看】58 道 Vue 常见面试题集锦,涵盖入门到精通,自测 Vue 掌握程度...

△ 是新朋友吗&#xff1f;记得先点web前端学习圈关注我哦&#xff5e;1.vue优点&#xff1f;答&#xff1a;轻量级框架&#xff1a;只关注视图层&#xff0c;是一个构建数据的视图集合&#xff0c;大小只有几十 kb &#xff1b;简单易学&#xff1a;国人开发&#xff0c;中文文…

购票啦 | 2020中国.NET开发者峰会启动

.NET Conf China 2020去年2019年在上海举行了第一届的中国.NET开发者峰会&#xff0c;今年2020年即将在苏州举行第二届&#xff0c;有兴趣的童鞋们&#xff0c;不要忘记购票参加哈。 1大会背景介绍2014年微软组织并成立.NET基金会&#xff0c;微软在成为主要的开源参与者的道路…

w7下如何安装linux双系统,ubuntu安装教程(下): 教你装win7+Ubuntu双系统

今天突发的想装个双系统玩玩&#xff0c;原来我ubuntu和win8都是装在虚拟机里面的&#xff0c;感觉用起来很不方便&#xff0c;而且感觉用起来特别不流畅&#xff0c;所以就想装个win7ubuntu的双系统。原来是想把ubuntu直接wubi安装在win7里面。但是这样的话&#xff0c;会带来…

vscode代码运行时间工具_10款实用的VSCode插件提升你的编辑体验 | 第98期

代码编辑器或者文本编辑器相信大家都不会陌生&#xff0c;但是&#xff0c;常用Windows的朋友大概都知道其自带的“文本编辑器”那是一款多么难用的软件。后来又有一系列的编辑器&#xff0c;比如notepad、sublime、atom等等&#xff0c;包括Linux用户喜欢的vim&#xff0c;这些…

简单理解线程同步上下文

为了线程安全&#xff0c;winform和wpf框架中规定只能使用UI线程操作控件&#xff0c;从其它线程上操作控件就会报跨线程异常。假如有这样一个场景&#xff1a;点击按纽&#xff0c;然后开始计算员工薪资&#xff0c;并将计算信息实时展示在一个文本框中&#xff0c;由于计算过…

嵌入式linux pcie网卡配置,嵌入式Linux下PCIE数据采集卡驱动开发

目录5.4 中断 (34)5.4.1 Linux中断处理架构 (34)5.4.2 Linux中断编程 (34)5.5 本章小结 (35)第六章PCIE高速数据采集卡驱动程序开发 (36)6.1 PCI EXPRESS的配置空间 (36)6.2 PCI EXPRESS的接口功能寄存器 (37)6.3 PCIE高速数据采集卡驱动程序开发 (38)6.3.1 驱动模块加载 (39)6…

修改手机屏幕刷新率_手机屏幕没有高刷新率算不上旗舰机?看看网友都是如何回答的...

近日Redmi K30 Pro的各种参数信息逐渐曝光&#xff0c;高通骁龙865处理器、LPDDR5内存、UFS3.1闪存、VC液冷散热、前置弹出式摄像头、多彩呼吸灯、Z轴线性马达等等参数都备受好评。唯独在提及屏幕时&#xff0c;60Hz的刷新率就让网友们不满意了。此前在K30的发布会上&#xff0…

持续交付二:为什么需要多个环境

关于开发测试生产需要多少个环境&#xff0c;因公司而异&#xff0c;这里分享一下我建议开发过程中使用的几个环境。Development环境&#xff0c;就是开发环境&#xff0c;程序写代码&#xff0c;写单元测试的环境&#xff0c;一般和IDE一起&#xff0c;经常是在调试模式下进行…

linux显示内存状态,Linux显示内存状态

Linux显示内存状态youhaidongyouhaidong-ThinkPad-Edge-E545:~$ freetotal used free shared buffers cachedMem: 3103064 1417636 1685428 6672 109496 572168-/ buffers/cache: 735972 2367092Swap: 3998716 0 3998716Linux显示内存统计最大和最小的详情Linux显示内存统计最大…

git revert 后再次merge_git如何回滚错误合并的分支

导读&#xff1a;分类&#xff1a;技术干货题目&#xff1a;git如何回滚错误合并的分支合并到线上分支出现问题的修复方式。场景线上分支&#xff1a;master你开发的分支&#xff1a;dev1同时开发的分支&#xff1a;dev2dev1分支开发的代码已经上线&#xff0c;并且已经merge到…

部署Dotnet Core应用到Kubernetes(二)

前一篇文章&#xff0c;概念性地介绍了K8s的一些基础组件&#xff0c;如Pod、部署和服务。这篇文章&#xff0c;我打算写写如何使用YAML清单定义和配置这些资源。实际上&#xff0c;在K8s集群中创建对象有几种方式 - 命令&#xff0c;或声明。两种方式区别不大。不过实际应用中…

linux+c+逐行读文件内容,使用C ++中的ifstream逐行读取文件

在C 中逐行读取文件可以通过某些不同的方式完成。[快]循环使用std :: getline()最简单的方法是使用std :: getline()调用打开std :: ifstream和循环。 代码简洁易懂。#include std::ifstream file(FILENAME);if (file.is_open()) {std::string line;while (getline(file, line)…

如何区分netty是udp还是tcp_鲜奶粉还是大包粉,到底该如何区分?

100%的宝爸宝妈们都希望为宝宝挑选最营养、最新鲜的口粮&#xff0c;也相信让娃爸每周逛遍各大超市&#xff0c;只为了选两罐最新日期的奶粉&#xff0c;这种事情一定不止一个人做过&#xff01;一直以来&#xff0c;人们对于“新鲜”有着亘古不变的追求。尤其是对待生活中的“…

Winform 进度条弹窗和任务控制

Winform 进度条弹窗和任务控制目录Winform 进度条弹窗和任务控制一、弹窗前台二、弹窗后台三、使用方法四、效果展示和代码地址独立观察员 2020 年 11 月 17 日最近要给一个 Winform 项目添加功能&#xff0c;需要一个能显示进度条的弹窗&#xff0c;还要求能够中止任务&#x…

linux下rip服务启动失败,RIP协议_linux系统管理与服务的技术博客_51CTO博客

理解并配置动态路由协议RIPRIP(Routing Information Protocol)是应用较早、使用较普遍的内部网关协议(Interior Gateway Protocol&#xff0c;IGP)&#xff0c;适用于小型网络&#xff0c;是典型的距离矢量(Distance-Vector)路由协议&#xff0c;是一种单纯的向邻居路由器发送自…

c语言prime函数怎么用_C语言 要发就发

点击上方“蓝字”关注我们愉快的一天&#xff0c;不得不做的三件事&#xff1a;种田&#xff0c;锄地&#xff0c;整代码&#xff01;&#xff01;&#xff01;【题目】“1898——要发就发”。请将不超过1993的所有素数从小到大排成第一行&#xff0c;第二行上的每个数都等于它…

linux如何使用vnc远程登录,如何使用Xmanager及VNC登录远程桌面

如何调用远程桌面&#xff0c;比较常见的有两种方式&#xff1a;Xmanager及VNC正好今天鼓捣了一下&#xff0c;特整理如下&#xff1a;XmanagerXmanager的调用也有两种方式&#xff1a;一、直接在Xshell中调用这时需设置会话属性&#xff0c;如下图所示&#xff0c;需在“隧道”…

python自然语言处理_python

chinese-xinhua/ | - data/ 汉字例子&#xff1a; { "word": "吖", "oldword": "吖", "strokes": "6", "pinyin": "ā", "radicals": "口", "explanation": &…

别在.NET死忠粉面前黑.NET5,它未来可期!

期盼已久的.NET5&#xff0c;终于在11月11日正式发布&#xff0c;整个.NET社区都为之沸腾&#xff0c;知乎、博客园、QQ群、微信群&#xff0c;随处可见.NET5 的热议&#xff0c;作为.NET死忠粉&#xff0c;我也挺兴奋的。然而一片欢歌笑语中总有一些很不和谐的声音&#xff0c…