1 2
| services.AddMediatR(assemblies);
|
把指定程序集(assemblies)里实现的 MediatR 处理程序注册到依赖注入容器,这样你就可以用 MediatR 的领域事件和命令模式了。
AddMediatR是自定义扩展方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| public static class MediatorExtensions { public static IServiceCollection AddMediatR(this IServiceCollection services, IEnumerable<Assembly> assemblies) { return services.AddMediatR(assemblies.ToArray()); }
public static async Task DispatchDomainEventsAsync(this IMediator mediator, DbContext ctx) { var domainEntities = ctx.ChangeTracker .Entries<IDomainEvents>() .Where(x => x.Entity.GetDomainEvents().Any());
var domainEvents = domainEntities .SelectMany(x => x.Entity.GetDomainEvents()) .ToList();
domainEntities.ToList() .ForEach(entity => entity.Entity.ClearDomainEvents());
foreach (var domainEvent in domainEvents) { await mediator.Publish(domainEvent); } } }
|
AddMediatR批量注册MediatR服务。
DispatchDomainEventsAsync派发 EF Core 聚合根的领域事件
- 找出有领域事件的实体
ctx.ChangeTracker.Entries<IDomainEvents>() 会获取当前跟踪的、实现了 IDomainEvents 接口的实体。
过滤出 GetDomainEvents().Any() 的实体,说明它们确实有未发布的领域事件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| public interface IDomainEvents { IEnumerable<INotification> GetDomainEvents();
0/8589+0lkmmnbgfvcdx5` 8\--098765 ';L';LK';LK/.,ML,KMJNHBGV;LOKIUHGFDIUY' void AddDomainEvent(INotification eventItem);
void AddDomainEventIfAbsent(INotification eventItem);
void ClearDomainEvents(); }
|
收集所有领域事件
SelectMany 把所有实体的事件扁平化成一个 List<IDomainEvent>。
清空实体的事件集合
避免同一个事件被重复发布。
逐个发布事件
mediator.Publish(domainEvent) 让 MediatR 找到对应的 INotificationHandler<T> 来处理事件。
添加工作单元过滤器
1 2 3 4 5
| services.Configure<MvcOptions>(options => { options.Filters.Add<UnitOfWorkFilter>(); });
|
ASP.NET Core 的 MVC 管道全局加了一个“工作单元(Unit of Work)过滤器”,让所有控制器/Action 执行时自动启用一个工作单元逻辑。
services.Configure<MvcOptions>
这是对 MVC 框架的配置入口,可以修改 MVC 的行为,比如格式化器、模型绑定、过滤器等。
options.Filters.Add<UnitOfWorkFilter>()
给 全局过滤器集合 添加了一个 UnitOfWorkFilter,所有 Controller Action 执行前后都会经过它。
这和在每个 Controller 上 [ServiceFilter(typeof(UnitOfWorkFilter))] 一样,但这是全局生效。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| public class UnitOfWorkFilter : IAsyncActionFilter { private static UnitOfWorkAttribute? GetUoWAttr(ActionDescriptor actionDesc) { var caDesc = actionDesc as ControllerActionDescriptor; if (caDesc == null) { return null; } var uowAttr = caDesc.ControllerTypeInfo .GetCustomAttribute<UnitOfWorkAttribute>(); if (uowAttr != null) { return uowAttr; } else { return caDesc.MethodInfo .GetCustomAttribute<UnitOfWorkAttribute>(); } }
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { var uowAttr = GetUoWAttr(context.ActionDescriptor); if (uowAttr == null) { await next(); return; } using TransactionScope txScope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled); List<DbContext> dbCtxs = new List<DbContext>(); foreach (var dbCtxType in uowAttr.DbContextTypes) { var sp = context.HttpContext.RequestServices; DbContext dbCtx = (DbContext)sp.GetRequiredService(dbCtxType); dbCtxs.Add(dbCtx); } var result = await next(); if (result.Exception == null) { foreach (var dbCtx in dbCtxs) { await dbCtx.SaveChangesAsync(); } txScope.Complete(); } } }
|
- GetUoWAttr查找UnitOfWorkAttribute
UnitOfWorkAttribute自定义的特性类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
public class UnitOfWorkAttribute : Attribute { public Type[] DbContextTypes { get; init; }
public UnitOfWorkAttribute(params Type[] dbContextTypes) { this.DbContextTypes = dbContextTypes; foreach (var type in dbContextTypes) { if (!typeof(DbContext).IsAssignableFrom(type)) { throw new ArgumentException($"Type {type.FullName} is not a DbContext type."); } } } }
|
OnActionExecutionAsync拦截 Action 方法执行,自动开启事务,确保多个 DbContext 操作要么全部成功,要么全部回滚。