我正在开发围绕模块化整体构建的项目。我已将 DbContext 分为模块、UnitOfWork 模式(希望如此)。我在保存到数据库时遇到问题。系统仅在最后注册的 DbContext 上工作,看不到其他的。我可以创建一个客户实体,但它不会保存到数据库。当我登录到某个客户时,我可以创建一个购物篮并向其中添加产品(因为最后注册的 DbContext 是 BasketsDbContext)。
我提供代码 - 所有模块看起来都一样,所以我只粘贴一次。
共享.基础设施.SqlServerUnitOfWork
public abstract class SqlServerUnitOfWork<T> : IUnitOfWork where T : DbContext
{
private readonly T _dbContext;
private readonly IDomainEventDispatcher _domainEventDispatcher;
protected SqlServerUnitOfWork(T dbContext, IDomainEventDispatcher domainEventDispatcher)
{
_dbContext = dbContext;
_domainEventDispatcher = domainEventDispatcher;
}
public async Task CommitAndDispatchDomainEventsAsync<TEntity>(TEntity entity) where TEntity : Entity
{
Log.Information("dbcontext: {@a}", _dbContext.GetType().Name);
await _domainEventDispatcher.DispatchDomainEvents(entity);
await _dbContext.SaveChangesAsync();
}
public async Task<int> CommitChangesAsync()
{
return await _dbContext.SaveChangesAsync();
}
}
共享.抽象.UnitOfWork
public interface IUnitOfWork
{
Task<int> CommitChangesAsync();
Task CommitAndDispatchDomainEventsAsync<TEntity>(TEntity entity)
where TEntity : Entity;
}
共享.基础设施.扩展
public static IServiceCollection AddUnitOfWork<T>(this IServiceCollection services) where T : class, IUnitOfWork
{
services.AddScoped<IUnitOfWork, T>();
services.AddScoped<T>();
using var serviceProvider = services.BuildServiceProvider();
serviceProvider.GetRequiredService<UnitOfWorkTypeRegistry>().Register<T>();
return services;
}
模块UoW注册
public static IServiceCollection AddCustomersInfrastructure(this IServiceCollection services, IConfiguration configuration)
{
services.AddDbContext<CustomersDbContext>(options =>
{
options.UseSqlServer(configuration.GetConnectionString("DefaultConnection"));
});
services.AddScoped<ICustomerRepository, CustomerRepository>();
services.AddUnitOfWork<CustomersUnitOfWork>();
return services;
}
客户工作单元
internal class CustomersUnitOfWork : SqlServerUnitOfWork<CustomersDbContext>
{
public CustomersUnitOfWork(CustomersDbContext dbContext,
IDomainEventDispatcher domainEventDispatcher)
: base(dbContext, domainEventDispatcher)
{
}
}
在处理程序中,我注入 IUnitOfWork 并尝试使用它,我可以轻松发布和处理事件,但它不会保存任何不是 BasketsDbContext (最后注册的)的内容
注册客户命令处理程序
public async Task<AuthenticationResult> Handle(SignUpCustomerCommand command, CancellationToken cancellationToken)
{
var emailCheck = await _customerRepository.GetCustomerByEmail(command.Email);
if (emailCheck != null)
{
throw new BadRequestException("Email in use");
}
var validator = new SignUpCustomerValidator();
validator.ValidateAndThrow(command);
//await CheckIfEmailIsFreeToUse(command.Email);
var passwordHash = _hashingService.GenerateHashPassword(command.Password);
var address = Address.CreateAddress(command.Country, command.City, command.Street, command.PostalCode);
var customer = Customer.Create(command.Email,
passwordHash,
command.Name,
command.LastName,
address,
command.TelephoneNumber);
await _customerRepository.Add(customer);
await _unitOfWork.CommitAndDispatchDomainEventsAsync(customer);
var token = _tokenManager.GenerateToken(customer.Id, customer.Email, customer.Role);
return new AuthenticationResult(customer.Id, token);
}
日志
[16:26:37 INF] Starting request: SignUpCustomerCommand, 07/20/2023 16:26:37 +02:00
[16:26:39 INF] dbcontext: BasketsDbContext
[16:26:39 INF] Domain event: {"Customer": {"Id": {"Value": "847e33eb-08e6-440b-94eb-cc3786a56944", "$type": "CustomerId"}, "Email": {"Value": "[email protected]", "$type": "Email"}, "PasswordHash": {"Value": "$2a$08$fiOMM8UZXcg/m1IF6afVvuhU37BghzvfseTYH0.r8jXQE.heAqTOS", "$type": "PasswordHash"}, "Name": {"Value": "string", "$type": "Name"}, "LastName": {"Value": "string", "$type": "LastName"}, "Address": {"Country": "string", "City": "string", "Street": "string", "PostalCode": "string", "$type": "Address"}, "TelephoneNumber": {"Value": "1", "$type": "TelephoneNumber"}, "Role": "customer", "DomainEvents": [], "$type": "Customer"}, "$type": "CustomerCreatedDomainEvent"}
[16:26:39 INF] Customer created at: 07/20/2023 14:26:39
[16:26:39 INF] Customer created at: 07/20/2023 16:26:39 +02:00
[16:26:40 WRN] Long running request: SignUpCustomerCommand, (2588 milliseconds) - 07/20/2023
16:26:40 +02:00, {"Email": "[email protected]", "Password": "string", "Name": "string",
"LastName": "string", "TelephoneNumber": "1", "Country": "string", "City": "string", "Street":
"string", "PostalCode": "string", "$type": "SignUpCustomerCommand"}
[16:26:40 INF] Completed request: SignUpCustomerCommand, 07/20/2023 16:26:40 +02:00
[16:26:40 INF] HTTP POST /api/Customers/SignUp responded 200 in 2765.2058 ms
我当然可以明确地写在处理程序中
_domainEventDispatcher.DispatchEvents(customer);
_customerRepository.Commit();
它工作得很好,但对于一些事件的复杂处理程序来说,它可能会很痛苦。
我通过添加 DbContext 的 IEnumerable 并使用跟踪更改的 IEnumerable 来完成这项工作。
public class UnitOfWork : IUnitOfWork
{
private readonly IEnumerable<DbContext> _dbContexts;
private readonly IDomainEventsDispatcher _domainEventsDispatcher;
public UnitOfWork(IEnumerable<DbContext> dbContexts,
IDomainEventsDispatcher domainEventsDispatcher)
{
_dbContexts = dbContexts;
_domainEventsDispatcher = domainEventsDispatcher;
}
public async Task<int> CommitAsync(CancellationToken cancellationToken = default)
{
await _domainEventsDispatcher.DispatchEventsAsync();
var dbContext = _dbContexts.Where(x => x.ChangeTracker.HasChanges()).FirstOrDefault();
if (dbContext is not null)
{
return await dbContext.SaveChangesAsync(cancellationToken);
}
return 0;
}
}