跳转到内容

工作单元

工作单元(Unit of Work)是一种设计模式,用于跟踪业务事务期间对象的所有更改,并将多个数据库操作作为单个事务提交。在 MiCake 中,工作单元支持 Lazy 和 Immediate 两种初始化模式,以满足不同的性能和一致性需求。

工作单元的核心职责:

  1. 跟踪变更:跟踪业务操作期间所有对象的变更
  2. 协调持久化:将所有变更作为一个事务提交
  3. 保证一致性:确保数据的完整性和一致性
  4. 管理事务:控制事务的开始、提交和回滚

ASP.NET Core 应用中的自动工作单元(推荐)

Section titled “ASP.NET Core 应用中的自动工作单元(推荐)”

当你使用了MiCake.AspNetCore模块时,当MiCakeAspNetUowOption.IsAutoUowEnabled选项被启用时(默认为true),工作单元会在每次 HTTP 请求开始时自动创建,并在请求结束时自动提交或回滚,用户无须关心工作单元的生命周期管理。

// Startup.cs 或 Program.cs, 默认开启自动工作单元
services.AddMiCakeWithDefault<MyModule, MyDbContext>();
// 可以手动设置,以禁用自动工作单元
services.AddMiCakeWithDefault<MyModule, MyDbContext>(options =>
{
options.AspNetConfig = asp =>
{
asp.UnitOfWork.IsAutoUowEnabled = false;
};
});

Controller 示例

public class OrderController : ControllerBase
{
private readonly IRepository<Order> _orderRepository;
public OrderController(IRepository<Order> orderRepository)
{
_orderRepository = orderRepository;
}
// ✅ 工作单元自动创建、提交或回滚
[HttpPost]
public async Task<IActionResult> CreateOrder(CreateOrderDto dto)
{
var order = new Order(dto.CustomerId, dto.Items);
await _orderRepository.AddAsync(order);
// UoW 会在 action 执行成功后自动提交
return Ok(order.Id);
}
}

对于非 Web 场景或需要精确控制的情况:

public class OrderService
{
private readonly IUnitOfWorkManager _uowManager;
private readonly IRepository<Order> _orderRepository;
public OrderService(
IUnitOfWorkManager uowManager,
IRepository<Order> orderRepository)
{
_uowManager = uowManager;
_orderRepository = orderRepository;
}
public async Task ProcessOrderAsync(int orderId)
{
// ✅ 使用 BeginAsync 创建工作单元(推荐)
using var uow = await _uowManager.BeginAsync();
var order = await _orderRepository.FindAsync(orderId);
order.Process();
// 提交事务
await uow.CommitAsync();
}
}

MiCake 提供了两种事务初始化模式:Lazy(延迟)和 Immediate(立即),以满足不同的性能和一致性需求。

事务在第一次数据库操作时才真正开启:

using var uow = await _uowManager.BeginAsync();
// 此时事务尚未开启
var order = await _orderRepository.FindAsync(1);
// 此时事务才开始
await uow.CommitAsync();
// 提交事务

特点

  • 性能最优:只在需要时才开启事务
  • 适合场景:读多写少的操作、可能不需要事务的场景
  • 延迟初始化:事务在第一次数据库操作时才创建

事务在创建工作单元时立即开启:

var options = new UnitOfWorkOptions
{
InitializationMode = TransactionInitializationMode.Immediate
};
using var uow = await _uowManager.BeginAsync(options);
// 事务立即开启
var order = await _orderRepository.FindAsync(1);
// 直接使用已开启的事务
await uow.CommitAsync();

特点

  • 一致性保证:确保事务从开始就存在
  • 适合场景:关键业务操作、需要明确事务边界的场景
  • 立即初始化:事务在 Begin 时就创建
场景推荐模式原因
常规 CRUD 操作Lazy性能更好,大部分操作都需要事务
关键业务操作Immediate确保事务一致性
可能回滚的操作Immediate避免延迟初始化带来的不确定性
高并发读操作Lazy减少不必要的事务开销
分布式事务Immediate需要明确的边界控制
// Controller 级别:所有 Action 使用立即初始化
[UnitOfWork(InitializationMode = TransactionInitializationMode.Immediate)]
public class CriticalOperationController : ControllerBase
{
// ...
}
// Action 级别:特定操作使用立即初始化
public class OrderController : ControllerBase
{
[UnitOfWork(InitializationMode = TransactionInitializationMode.Immediate)]
public async Task<IActionResult> CriticalOperation()
{
// 事务在 action 开始前就已开启
}
}

MiCake 支持嵌套工作单元,内层工作单元会自动加入外层事务:

public async Task ComplexOperationAsync()
{
// 外层工作单元
using var outerUow = await _uowManager.BeginAsync();
var order = await _orderRepository.FindAsync(1);
order.Update();
// 内层工作单元(自动嵌套)
using var innerUow = await _uowManager.BeginAsync();
var product = await _productRepository.FindAsync(1);
product.DecreaseStock();
await innerUow.CommitAsync(); // 标记内层完成
await outerUow.CommitAsync(); // 统一提交
}

嵌套规则

  • 内层事务自动加入外层事务
  • 只有最外层工作单元负责最终提交
  • 如果任意层失败,整个事务回滚
  • 支持多层嵌套(建议不超过 3 层)
[UnitOfWork]
public class ProductController : ControllerBase
{
// 所有 Action 都会自动创建 UoW
}
[DisableUnitOfWork]
public class ReportController : ControllerBase
{
// 纯查询 Controller,不需要事务
}

或在 Action 级别覆盖:

public class MixedController : ControllerBase
{
// 默认启用 UoW
[DisableUnitOfWork]
public async Task<IActionResult> GetCachedData()
{
// 此 Action 不创建 UoW
}
}
[UnitOfWork(IsolationLevel = IsolationLevel.Serializable)]
public async Task<IActionResult> HighConsistencyOperation()
{
// 使用最高隔离级别
}

MiCake 会自动识别只读操作(根据 Action 名称):

public class OrderController : ControllerBase
{
// 自动识别为只读(跳过事务提交)
public async Task<IActionResult> GetOrder(int id) { }
public async Task<IActionResult> FindOrders() { }
public async Task<IActionResult> QueryOrders() { }
public async Task<IActionResult> SearchOrders() { }
}

可通过配置自定义只读关键字:

services.AddMiCakeWithDefault<MyModule, MyDbContext>(
miCakeAspNetConfig: options =>
{
options.UnitOfWork.ReadOnlyActionKeywords = ["Get", "Find", "Query", "Search", "List", "Fetch"];
});
services.AddMiCakeWithDefault<MyModule, MyDbContext>(
miCakeAspNetConfig: options =>
{
options.UnitOfWork.IsAutoTransactionEnabled = false;
});

此时需要手动管理所有工作单元。

在长事务中创建保存点,支持部分回滚:

using var uow = await _uowManager.BeginAsync();
// 执行一些操作
await ProcessStep1();
// 创建保存点
var savepoint = await uow.CreateSavepointAsync("step1");
try
{
// 执行可能失败的操作
await ProcessStep2();
}
catch
{
// 回滚到保存点,保留 step1 的更改
await uow.RollbackToSavepointAsync("step1");
}
await uow.CommitAsync();
using var uow = await _uowManager.BeginAsync();
try
{
await ProcessOrder();
if (someCondition)
{
// 手动回滚
await uow.RollbackAsync();
return;
}
await uow.CommitAsync();
}
catch
{
// 异常时自动回滚
throw;
}
using var uow = await _uowManager.BeginAsync();
uow.OnCommitting += (sender, args) =>
{
_logger.LogInformation("UoW {Id} is committing", args.UnitOfWorkId);
};
uow.OnCommitted += (sender, args) =>
{
_logger.LogInformation("UoW {Id} committed successfully", args.UnitOfWorkId);
};
uow.OnRolledBack += (sender, args) =>
{
_logger.LogWarning(args.Exception, "UoW {Id} rolled back", args.UnitOfWorkId);
};
await ProcessOrder();
await uow.CommitAsync();
services.AddMiCakeWithDefault<MyModule, MyDbContext>(
miCakeAspNetConfig: options =>
{
// 启用/禁用自动事务(默认:true)
options.UnitOfWork.IsAutoTransactionEnabled = true;
// 只读 Action 关键字
options.UnitOfWork.ReadOnlyActionKeywords = ["Find", "Get", "Query", "Search"];
});
var options = new UnitOfWorkOptions
{
// 隔离级别(默认:ReadCommitted)
IsolationLevel = IsolationLevel.ReadCommitted,
// 初始化模式(默认:Lazy)
InitializationMode = TransactionInitializationMode.Lazy,
// 是否只读(默认:false)
IsReadOnly = false,
// 超时时间(秒)
Timeout = 30
};
using var uow = await _uowManager.BeginAsync(options);
  1. 优先使用 BeginAsync()
// ✅ 好
using var uow = await _uowManager.BeginAsync();
// ❌ 避免(仅用于向后兼容)
using var uow = _uowManager.Begin();
  1. 使用 using 语句确保 Dispose
// ✅ 好
using var uow = await _uowManager.BeginAsync();
// ... 操作
await uow.CommitAsync();
// ❌ 差
var uow = await _uowManager.BeginAsync();
// ... 操作
await uow.CommitAsync();
// 忘记 Dispose!
  1. 明确提交或回滚
using var uow = await _uowManager.BeginAsync();
try
{
// ... 操作
await uow.CommitAsync(); // ✅ 明确提交
}
catch
{
// Dispose 时会记录警告
throw;
}
  1. 合理使用 Attribute
// ✅ 在 Controller 级别声明,减少重复
[UnitOfWork]
public class OrderController : ControllerBase { }
// ✅ Action 级别覆盖特殊情况
[DisableUnitOfWork]
public async Task<IActionResult> GetCachedData() { }
  1. 不要在循环中创建多个 UoW
// ❌ 差
foreach (var order in orders)
{
using var uow = await _uowManager.BeginAsync();
await ProcessOrder(order);
await uow.CommitAsync();
}
// ✅ 好
using var uow = await _uowManager.BeginAsync();
foreach (var order in orders)
{
await ProcessOrder(order);
}
await uow.CommitAsync();
  1. 不要在 UoW 外部使用 Repository
// ❌ 差
var order = await _orderRepository.FindAsync(1); // 没有 UoW 上下文
// ✅ 好
using var uow = await _uowManager.BeginAsync();
var order = await _orderRepository.FindAsync(1);
// ... 操作
await uow.CommitAsync();
  1. 避免过长的事务
// ❌ 差
using var uow = await _uowManager.BeginAsync();
await DoLotsOfWork(); // 10 分钟的操作
await DoMoreWork();
await uow.CommitAsync();
// ✅ 好 - 将长操作拆分
await DoLotsOfWork(); // 不在事务中
using var uow = await _uowManager.BeginAsync();
await DoCriticalWork(); // 只有关键部分在事务中
await uow.CommitAsync();
// 错误:未明确提交或回滚
using var uow = await _uowManager.BeginAsync();
await ProcessOrder();
// 忘记调用 CommitAsync() 或 RollbackAsync()
// Dispose 时会记录警告:"UnitOfWork disposed without being completed"
using var uow = await _uowManager.BeginAsync();
try
{
await ProcessOrder();
await uow.CommitAsync(); // ✅ 明确提交
}
catch (Exception ex)
{
await uow.RollbackAsync(); // ✅ 明确回滚
throw;
}
// 错误:使用 requiresNew 创建新事务
using var outerUow = await _uowManager.BeginAsync();
using var innerUow = await _uowManager.BeginAsync(requiresNew: true);
// 这会创建两个独立的事务,而不是嵌套
// 正确:不使用 requiresNew 参数
using var outerUow = await _uowManager.BeginAsync();
using var innerUow = await _uowManager.BeginAsync();
// 内层会自动嵌套在外层事务中
await innerUow.CommitAsync();
await outerUow.CommitAsync();

工作单元是 MiCake 中事务管理的核心,在框架中:

  • Lazy 和 Immediate 模式 - 满足不同性能和一致性需求
  • 嵌套事务支持 - 灵活的事务组合
  • 声明式控制 - Attribute 简化配置
  • 自动管理 - ASP.NET Core 集成

通过合理使用工作单元,您可以:

  • 确保数据一致性
  • 简化事务管理
  • 提高代码可维护性
  • 优化应用性能

下一步: