-
Notifications
You must be signed in to change notification settings - Fork 893
2881099 edited this page Dec 16, 2025
·
75 revisions
dotnet add package FreeSql.DbContext
UnitOfWork 是对 DbTransaction 事务对象的封装,方便夹带私有数据。
using (var uow = fsql.CreateUnitOfWork())
{
await uow.Orm.Insert(item).ExecuteAffrowsAsync(); //uow.Orm API 和 IFreeSql 一样
await uow.Orm.Ado.ExecuteNoneQueryAsync(sql);
await fsql.Insert(item)... //错误,不在一个事务
var repo = uow.GetRepository<Song>(); //仓储 CRUD
await repo.InsertAsync(item);
uow.Commit();
}提示:uow 范围内,尽量别使用 fsql 对象,以免不处在一个事务
使用 UnitOfWorkManager 管理 UnitOfWork,如下:
using (var uowManager = new UnitOfWorkManager(fsql))
{
using (var uow = uowManager.Begin())
{
using (var uow2 = uowManager.Begin()) //与 uow 同一个事务
{
uow2.Commit(); //事务还未提交
}
uow.Commit(); //事务提交
}
}var builder = WebApplication.CreateBuilder(args);
Func<IServiceProvider, IFreeSql> fsqlFactory = r =>
{
IFreeSql fsql = new FreeSql.FreeSqlBuilder()
.UseConnectionString(FreeSql.DataType.Sqlite, @"Data Source=freedb.db")
.UseMonitorCommand(cmd => Console.WriteLine($"Sql:{cmd.CommandText}"))
.Build();
return fsql;
};
builder.Services.AddSingleton<IFreeSql>(fsqlFactory);
builder.Services.AddFreeRepository();
builder.Services.AddScoped<UnitOfWorkManager>();
builder.Services.AddScoped<SongService>();
WebApplication app = builder.Build();
public class SongService
{
readonly IBaseRepository<Song> _songRepository;
readonly IBaseRepository<Detail> _detailRepository;
public SongService(IBaseRepository<Song> songRepository, IBaseRepository<Detail> detailRepository)
{
_songRepository = songRepository;
_detailRepository = detailRepository;
}
[Transactional]
async public Task Test1()
{
//所有注入的仓储对象,都是一个事务
await _songRepository.InsertAsync(xxx1);
await _detailRepository.DeleteAsync(xxx2);
this.Test2();
}
[Transactional(Propagation = Propagation.Nested)]
public void Test2() //嵌套事务
{
}
}具体请移步文档:- AOP 特性标签实现跨方法事务
同线程事务内置在 FreeSql.dll,由 fsql.Transaction 管理事务提交回滚(缺点:不支持异步)。
用户购买了价值 100 元的商品:扣余额、扣库存。
fsql.Transaction(() =>
{
//fsql.Ado.TransactionCurrentThread 获得当前事务对象
var affrows = fsql.Update<User>()
.Set(a => a.Wealth - 100)
.Where(a => a.Wealth >= 100).ExecuteAffrows();
//判断别让用户余额扣成负数
//抛出异常,回滚事务,事务退出
if (affrows < 1) throw new Exception("用户余额不足");
affrows = fsql.Update<Goods>()
.Set(a => a.Stock - 1)
.Where(a => a.Stock >= 1).ExecuteAffrows();
if (affrows < 1) throw new Exception("商品库存不足");
});同线程事务使用简单,需要注意的限制:
-
事务对象在线程挂载,每个线程只可开启一个事务连接,嵌套使用的是同一个事务;
-
事务体内代码不可以切换线程,因此不可使用任何异步方法,包括 FreeSql 提供的数据库异步方法(可以使用任何 Curd 同步方法);
var user = fsql.Select<User>().ForUpdate(true).Where(a => a.Id == 1).ToOne();
//SELECT ... FROM User a for update nowaitfor update 在 Oracle/PostgreSQL/MySql 是通用的写法,我们对 SqlServer 做了特别适配,执行的 SQL 语句大致如下:
SELECT ... FROM [User] a With(UpdLock, RowLock, NoWait)// 场景:已经有一个开启的 Connection 和 Transaction
using (var conn = new SqlConnection("..."))
{
conn.Open();
using (var tran = conn.BeginTransaction())
{
// 1. 原生/Dapper 操作
// command.Transaction = tran;
// command.ExecuteNonQuery();
// 2. 桥接给 FreeSql
// 使用扩展方法创建适配器,传入现有的 tran
using (var uow = fsql.CreateUnitOfWork(tran))
{
// 在此 uow 下获取的仓储或 Orm,都会使用传入的 tran
var repo = uow.GetRepository<MyEntity>();
repo.Insert(new MyEntity { Name = "FreeSql Insert" });
// 或者直接使用 Orm
uow.Orm.Insert(new MyEntity { Name = "Direct Orm Insert" }).ExecuteAffrows();
// 这里的 Commit 只会触发 FreeSql 的事件,不会提交物理事务
uow.Commit();
}
// 3. 真正的提交由最外层控制
tran.Commit();
}
}