![]() |
VOOZH | about |
dotnet add package FreeSql.Cloud --version 2.0.1
NuGet\Install-Package FreeSql.Cloud -Version 2.0.1
<PackageReference Include="FreeSql.Cloud" Version="2.0.1" />
<PackageVersion Include="FreeSql.Cloud" Version="2.0.1" />Directory.Packages.props
<PackageReference Include="FreeSql.Cloud" />Project file
paket add FreeSql.Cloud --version 2.0.1
#r "nuget: FreeSql.Cloud, 2.0.1"
#:package FreeSql.Cloud@2.0.1
#addin nuget:?package=FreeSql.Cloud&version=2.0.1Install as a Cake Addin
#tool nuget:?package=FreeSql.Cloud&version=2.0.1Install as a Cake Tool
<h1 align="center"> 🦄 FreeSql.Cloud </h1>
为 FreeSql 提供跨数据库访问,分布式事务TCC、SAGA解决方案,支持 .NET Core 2.1+, .NET Framework 4.0+.
dotnet add package FreeSql.Cloud
or
Install-Package FreeSql.Cloud
public enum DbEnum { db1, db2 }
public class FreeSqlCloud : FreeSqlCloud<DbEnum>
{
public FreeSqlCloud() : base(null) { }
public FreeSqlCloud(string distributeKey) : base(distributeKey) { }
}
var fsql = new FreeSqlCloud();
fsql.DistributeTrace = log => Console.WriteLine(log.Split('\n')[0].Trim());
fsql.Register(DbEnum.db1, () => new FreeSqlBuilder().UseConnectionString(DataType.Sqlite, @"Data Source=db1.db").Build());
fsql.Register(DbEnum.db2, () => new FreeSqlBuilder().UseConnectionString(DataType.Sqlite, @"Data Source=db2.db").Build());
services.AddSingleton<IFreeSql>(fsql);
services.AddSingleton(fsql);
FreeSqlCloud 必须定义成单例模式
new FreeSqlCloud() 多连接管理,DbEnum 换成 string 就是多租户管理
new FreeSqlCloud("myapp") 开启 TCC/SAGA 事务生效
FreeSqlCloud 的访问方式和 IFreeSql 一样:
fsql.Select<T>();
fsql.Insert<T>();
fsql.Update<T>();
fsql.Delete<T>();
//...
切换数据库(多线程安全):
fsql.Change(DbEnum.db2).Select<T>();
//同一线程,或异步await 后续 fsql.Select/Insert/Update/Delete 操作是 db2
fsql.Use(DbEnum.db2).Select<T>();
//单次有效
using (fsql.Change(DbEnum.db2)) {
//todo..
}
//FreeSql.Cloud v1.6.8 一个范围内切换,之后再切换回去
自动定向数据库配置:
fsql.EntitySteering = (_, e) =>
{
if (e.EntityType == typeof(User)) e.DBKey = DbEnum.db2;
//查询 User 自动定向 db2
};
1、静态仓储对象
FreeSql.Repository/UnitOfWorkManager 对象创建时固定了 IFreeSql,因此无法跟随 FreeSqlCloud 切换数据库。
注意:是同一个对象实例创建之后,无法跟随切换,创建新对象实例不受影响。
租户分库场景 Repository/UnitOfWorkManager 创建之前,先调用 fsql.Change 切换好数据库。
《FreeSql.Cloud 如何使用 UnitOfWorkManager 实现 AOP 事务?》
2、动态创建对象(不推荐)
但是。。。仍然有一种特殊需求,Repository 在创建之后,仍然能跟随 fsql.Change 切换数据库。
var repo = DB.Cloud.GetCloudRepository<User>();
DB.Cloud.Change(DbEnum.db2);
Console.WriteLine(repo.Orm.Ado.ConnectionString); //repo -> db2
DB.Cloud.Change(DbEnum.db1);
Console.WriteLine(repo.Orm.Ado.ConnectionString); //repo -> db1
这种机制太不可控,所以只做了简单的扩展方法创建,并不推荐 Ioc 注入。
FreeSqlCloud 内部使用 IdleBus + AsyncLocal<string> 方式实现,Change/Use 多线程并发是安全的。
FreeSqlCloud 实现了接口 IFreeSql,但它不负责直接交互数据库,只是个代理层。
public class FreeSqlCloud<TDBKey> : IFreeSql
{
AsyncLocal<TDBKey> _currentKey = new AsyncLocal<TDBKey>();
IFreeSql _current => _idlebus.Get(_currentKey.Value);
IdleBus<TDBKey, IFreeSql> _idlebus;
...
public IAdo Ado => _current.Ado;
public GlobalFilter GlobalFilter => _current.GlobalFilter;
public void Transaction(Action handler) => _current.Transaction(handler);
...
}
AsyncLocal 负责存储执行上下文 DBKey 值,在异步或同步并发场景是安全的,fsql.Change(DbEnum.db2) 会改变该值。fsql.Change/Use 方法返回 IFreeSql 特殊实现,大大降低 IdleBus 因误用被释放的异常(原因:IdleBus.Get 返回值不允许被外部变量长期引用,应每次 Get 获取对象)
1、简介
FreeSqlCloud 提供 TCC/SAGA 分布式事务调度、失败重试、持久化重启后重新唤醒事务单元、等管理功能。
TCC 事务特点:
// 测试数据
fsql.Use(DbEnum.db1).Insert(new User { Id = 1, Name = "testuser01", Point = 10 }).ExecuteAffrows();
fsql.Use(DbEnum.db2).Insert(new Goods { Id = 1, Title = "testgoods01", Stock = 0 }).ExecuteAffrows();
var orderId = Guid.NewGuid();
await fsql.StartTcc(orderId.ToString(), "支付购买",
new TccOptions
{
MaxRetryCount = 10,
RetryInterval = TimeSpan.FromSeconds(10)
})
.Then<Tcc1>(DbEnum.db1, new BuyUnitState { UserId = 1, Point = 10, GoodsId = 1, OrderId = orderId })
.Then<Tcc2>(DbEnum.db2, new BuyUnitState { UserId = 1, Point = 10, GoodsId = 1, OrderId = orderId })
.Then<Tcc3>(DbEnum.db2, new BuyUnitState { UserId = 1, Point = 10, GoodsId = 1, OrderId = orderId })
.ExecuteAsync();
2022-08-16 10:47:53 【myapp】db1 注册成功, 并存储 TCC/SAGA 事务相关数据
2022-08-16 10:47:53 【myapp】成功加载历史未完成 TCC 事务 0 个
2022-08-16 10:47:53 【myapp】成功加载历史未完成 SAGA 事务 0 个
2022-08-16 10:47:53 【myapp】TCC (3a9c548f-95b1-43b4-b918-9c3817d4c316, 支付购买) Created successful, retry count: 10, interval: 10S
2022-08-16 10:47:53 【myapp】TCC (3a9c548f-95b1-43b4-b918-9c3817d4c316, 支付购买) Unit1(第1步:数据库db1 扣除用户积分) TRY successful
2022-08-16 10:47:53 【myapp】数据库使用[Use] db2
2022-08-16 10:47:53 【myapp】TCC (3a9c548f-95b1-43b4-b918-9c3817d4c316, 支付购买) Unit2(第2步:数据库db2 扣除库存) TRY failed, ready to CANCEL, -ERR 扣除库存失败
2022-08-16 10:47:53 【myapp】TCC (3a9c548f-95b1-43b4-b918-9c3817d4c316, 支付购买) Unit1(第1步:数据库db1 扣除用户积分) CANCEL successful
2022-08-16 10:47:53 【myapp】TCC (3a9c548f-95b1-43b4-b918-9c3817d4c316, 支付购买) Completed, all units CANCEL successfully
SAGA 事务特点:
2、唯一标识
FreeSqlCloud 使用唯一标识区分,解决冲突问题,举例:
var fsql = new FreeSqlCloud("myapp");
var fsql2 = new FreeSqlCloud("myapp2");
fsql2 访问不到 fsql 产生的分布式事务,如果 webapi 部署多实例,只需要设置实例各自对应的 name 区分即可。
3、持久化
fsql.Register 第一个注册的称之为【主库】,存储 TCC/SAGA 持久数据,程序启动的时候,会将未处理完的事务载入内存重新调度。
自动创建表 tcc_myapp、saga_myapp:
提示:fsql2 会创建表 tcc_myapp2、saga_myapp2
| 字段名 | 描述 |
|---|---|
| tid | 事务ID |
| title | 事务描述,查看日志更直观 |
| total | 所有单元数量 |
| create_time | 创建时间 |
| finish_time | 完成时间 |
| status | Pending, Confirmed, Canceled, ManualOperation |
| max_retry_count | 最大重试次数,如果仍然失败将转为【人工干预】 |
| retry_interval | 重试间隔(秒) |
| retry_count | 已重试次数 |
| retry_time | 最后重试时间 |
自动创建表 tcc_myapp_unit、saga_myapp_unit:
提示:fsql2 会创建表 tcc_myapp2_unit、saga_myapp2_unit
| 字段名 | 描述 |
|---|---|
| tid | 事务ID |
| index | 单元下标,1到N |
| description | 单元描述,使用 [Description("xx")] 特性设置,查看日志更直观 |
| stage | Try, Confirm, Cancel |
| type_name | 对应 c# TccUnit/SagaUnit 反射类型信息,用于创建 TccUnit/SagaUnit 对象 |
| state | 状态数据 |
| state_type_name | 状态数据对应的 c# 反射类型信息 |
| create_time | 创建时间 |
| db_key | 用于唤醒时使用 fsql.Use(db_key) 对应的事务或开启事务 |
其他库会创建表 myapp_unit_invoked 判断重复执行
4、单元
TccUnit、SagaUnit 方法内可以使用 Orm 访问当前事务对象。
单元方法除了操作数据库,也支持远程访问 webapi/grpc,发生异常时触发重试调度。由于网络不确定因素,较坏的情况比如单元调用 webapi/grpc 成功,但是 tcc_unit 表保存状态失败,导致单元又会重试执行,所以 web/grpc 提供方应该保证幂等操作,无论多少次调用结果都一致。
// HTTP 服务编排??
var orderId = Guid.NewGuid();
await DB.Cloud.StartSaga(orderId.ToString(), "支付购买webapi(saga)",
new SagaOptions
{
MaxRetryCount = 10,
RetryInterval = TimeSpan.FromSeconds(10)
})
.Then<HttpSaga>(default, new HttpUnitState
{
Url = "https://192.168.1.100/saga/UserPoint",
Data = "UserId=1&Point=10&GoodsId=1&OrderId=" + orderId
})
.Then<HttpSaga>(default, new HttpUnitState
{
Url = "https://192.168.1.100/saga/GoodsStock",
Data = "UserId=1&Point=10&GoodsId=1&OrderId=" + orderId
})
.Then<HttpSaga>(default, new HttpUnitState
{
Url = "https://192.168.1.100/saga/OrderNew",
Data = "UserId=1&Point=10&GoodsId=1&OrderId=" + orderId
})
.ExecuteAsync();
class HttpSaga : SagaUnit<HttpUnitState>
{
public override Task Commit()
{
//Console.WriteLine("请求 webapi:" + State.Url + "/Commit" + State.Data);
return Task.CompletedTask;
}
public override Task Cancel()
{
//Console.WriteLine("请求 webapi:" + State.Url + "/Cancel" + State.Data);
return Task.CompletedTask;
}
}
class HttpUnitState
{
public string Url { get; set; }
public string Data { get; set; }
}
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 net5.0 was computed. net5.0-windows net5.0-windows was computed. net6.0 net6.0 was computed. net6.0-android net6.0-android was computed. net6.0-ios net6.0-ios was computed. net6.0-maccatalyst net6.0-maccatalyst was computed. net6.0-macos net6.0-macos was computed. net6.0-tvos net6.0-tvos was computed. net6.0-windows net6.0-windows was computed. net7.0 net7.0 was computed. net7.0-android net7.0-android was computed. net7.0-ios net7.0-ios was computed. net7.0-maccatalyst net7.0-maccatalyst was computed. net7.0-macos net7.0-macos was computed. net7.0-tvos net7.0-tvos was computed. net7.0-windows net7.0-windows was computed. net8.0 net8.0 was computed. net8.0-android net8.0-android was computed. net8.0-browser net8.0-browser was computed. net8.0-ios net8.0-ios was computed. net8.0-maccatalyst net8.0-maccatalyst was computed. net8.0-macos net8.0-macos was computed. net8.0-tvos net8.0-tvos was computed. net8.0-windows net8.0-windows was computed. net9.0 net9.0 was computed. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 was computed. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 netcoreapp2.0 was computed. netcoreapp2.1 netcoreapp2.1 was computed. netcoreapp2.2 netcoreapp2.2 was computed. netcoreapp3.0 netcoreapp3.0 was computed. netcoreapp3.1 netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 netstandard2.0 is compatible. netstandard2.1 netstandard2.1 was computed. |
| .NET Framework | net40 net40 is compatible. net403 net403 was computed. net45 net45 was computed. net451 net451 was computed. net452 net452 was computed. net46 net46 was computed. net461 net461 is compatible. net462 net462 was computed. net463 net463 was computed. net47 net47 was computed. net471 net471 was computed. net472 net472 was computed. net48 net48 was computed. net481 net481 was computed. |
| MonoAndroid | monoandroid monoandroid was computed. |
| MonoMac | monomac monomac was computed. |
| MonoTouch | monotouch monotouch was computed. |
| Tizen | tizen40 tizen40 was computed. tizen60 tizen60 was computed. |
| Xamarin.iOS | xamarinios xamarinios was computed. |
| Xamarin.Mac | xamarinmac xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos xamarinwatchos was computed. |
Showing the top 5 NuGet packages that depend on FreeSql.Cloud:
| Package | Downloads |
|---|---|
|
HandeSoft.Core.Abstract
Package Description |
|
|
RsCode
快速高效的开发.net就用RsCode,文档访问https://rscode.cn |
|
|
Shinya.Orm.FreeSql
Shinya.Framework |
|
|
Athena.Infrastructure.FreeSql
ORM基于FreeSql的实现 |
|
|
Cszy.Data.FreeSql
Cszy.Data.FreeSql是Cszy应用框架对FreeSql的封装,支持多租户,租户隔离,租户数据库隔离,租户数据库迁移,事务管理等封装 |
Showing the top 1 popular GitHub repositories that depend on FreeSql.Cloud:
| Repository | Stars |
|---|---|
|
leooneone/aibpm.plus
AIBPM是一个开源的工作流引擎。本项目是后端服务,前端请移步aibpm.ui.plus。
|
| Version | Downloads | Last Updated |
|---|---|---|
| 2.0.1 | 28,203 | 4/15/2025 |
| 2.0.0 | 1,031 | 4/3/2025 |
| 1.9.1 | 30,136 | 7/15/2024 |
| 1.9.0 | 1,212 | 7/5/2024 |
| 1.8.3 | 9,732 | 2/5/2024 |
| 1.8.2 | 3,215 | 12/20/2023 |
| 1.8.1 | 479 | 12/18/2023 |
| 1.6.12 | 812 | 12/7/2023 |
| 1.6.6 | 10,660 | 5/30/2023 |
| 1.6.5 | 21,795 | 3/29/2023 |
| 1.6.4 | 4,225 | 3/14/2023 |
| 1.6.3 | 27,673 | 9/16/2022 |
| 1.6.2 | 624 | 9/13/2022 |
| 1.6.1 | 796 | 8/31/2022 |