我正在开发一个面向 .NET 5 的 ASP.NET API,我需要在其中访问三个不同的 SQL 数据库。
我使用 AutoFac 作为我的 DI(免责声明:我是 AutoFac 的新手,过去只在 DI 中使用过 ASP.NET Core 构建)。
我也在使用 Steve Smith 的 CleanArchitecture 框架(https://github.com/ardalis/CleanArchitecture)
我正在使用通用存储库,每个 DbContext 一个(代表 3 个不同的数据库)。
我的启动项目的 Startup.cs --> ConfigureContainer 方法中有以下代码;
public void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterModule(new DefaultCoreModule());
builder.RegisterModule(new DefaultInfrastructureModule(_env.EnvironmentName == "Development"));
}
在我的 DefaultInfrastructureModeule 类中,我有以下代码;
private void RegisterCommonDependencies(ContainerBuilder builder)
{
builder.RegisterGeneric(typeof(IdpRepository<>))
.As(typeof(IRepository<>))
.As(typeof(IReadRepository<>))
.InstancePerLifetimeScope();
builder.RegisterGeneric(typeof(CdRepository<>))
.As(typeof(IRepository<>))
.As(typeof(IReadRepository<>))
.InstancePerLifetimeScope();
builder.RegisterGeneric(typeof(M1Repository<>))
.As(typeof(IRepository<>))
.As(typeof(IReadRepository<>))
.InstancePerLifetimeScope();
...
...
...
}
项目编译并运行。但是,当我尝试从存储库调用方法(例如 ListAsync 方法)时,唯一正常工作的存储库是注册序列中列出的最后一个存储库。
例如,使用上面代码中列出的顺序,对 M1Repository 的 ListAsync 调用按预期工作,但对 IdpRepository 或 CdRepository 中的 ListAsync 方法的调用失败并出现 SqlException;
Microsoft.Data.SqlClient.SqlException (0x80131904): Invalid object name 'User'.
at Microsoft.Data.SqlClient.SqlCommand.<>c.<ExecuteDbDataReaderAsync>b__169_0(Task`1 result)
就像它不理解 DbContext.Set 应该查询用户表(复数),而不是用户表。
现在,如果我重新排列 DI 注册的顺序并将 IdpRepository 的注册移动到按顺序注册的最后一个,则 ListAsync 调用随后适用于 IdpRepository 但随后对 M1Repository 和 CdRepository 的调用失败并出现类似的 SQL 异常.
无论我以什么顺序注册它们,只有最后一个注册的工作正常。
所有三个通用存储库都使用 CleanArchitecture 模板中使用的相同基本设计。CdRepository 示例如下所示;
public class CdRepository<T> : RepositoryBase<T>, IReadRepository<T>, IRepository<T>
where T : class
{
public CdRepository(CdDbContext dbContext)
: base(dbContext)
{
}
}
我确信这只是我不知道的 AutoFac 问题,因为我是 AutoFac 初学者。我似乎无法解决它。
有任何想法吗?
[更新 1]
这是我的 ListDepartments 端点,我在其中注入了一个存储库。
public class ListDepartments : BaseAsyncEndpoint
.WithRequest<DepartmentListRequest>
.WithResponse<DepartmentListResponse>
{
private readonly IReadRepository<Department> _repository;
public ListDepartments(IReadRepository<Department> repository)
{
_repository = repository;
}
[HttpGet("/Organizations/{OrganizationId}/Departments")]
[SwaggerOperation(
Summary = "Gets a list of all Departments for the specified Organization ID",
Description = "Gets a list of all Departments for the specified Organization ID",
OperationId = "Department.ListDepartments",
Tags = new[] { "OrganizationEndpoints" })
]
public override async Task<ActionResult<DepartmentListResponse>> HandleAsync([FromQuery] DepartmentListRequest request, CancellationToken cancellationToken)
{
var response = new DepartmentListResponse();
response.OrganizationId = request.OrganizationId;
response.Departments = (await _repository.ListAsync(cancellationToken))
.Select(department => new DepartmentListSummaryRecord(department.Id, department.Name))
.ToList();
return Ok(response);
}
}
[更新 2]
在阅读了@ssmith 的评论后,我意识到我需要为 3 个通用存储库中的每一个提供独特的接口。导致问题的原因是使用 CleanArchitecture 模板中提供的 IRepository 和 IReadRepository 接口用于 AutoFac 中的三个不同存储库注册中的每一个。就我而言,一个明显的大脑放屁的例子。
一旦我为三个存储库中的每一个创建了独特的接口,该解决方案现在就可以工作了。
这是修改后的存储库;
public class CdRepository<T> : RepositoryBase<T>, ICdReadRepository<T>, ICdRepository<T>
where T : class
{
public CdRepository(CdDbContext dbContext)
: base(dbContext)
{
}
}
public class IdpRepository<T> : RepositoryBase<T>, IIdpReadRepository<T>, IIdpRepository<T>
where T : class
{
public IdpRepository(IdpDbContext dbContext)
: base(dbContext)
{
}
}
public class M1Repository<T> : RepositoryBase<T>, IM1ReadRepository<T>, IM1Repository<T>
where T : class
{
public M1Repository(M1DbContext dbContext)
: base(dbContext)
{
}
}
这是我在其中注册存储库的 DefaultInfrastructureModule 类的修订版。
private void RegisterCommonDependencies(ContainerBuilder builder)
{
builder.RegisterGeneric(typeof(IdpRepository<>))
.As(typeof(IIdpRepository<>))
.As(typeof(IIdpReadRepository<>))
.InstancePerLifetimeScope();
builder.RegisterGeneric(typeof(CdRepository<>))
.As(typeof(ICdRepository<>))
.As(typeof(ICdReadRepository<>))
.InstancePerLifetimeScope();
builder.RegisterGeneric(typeof(M1Repository<>))
.As(typeof(IM1Repository<>))
.As(typeof(IM1ReadRepository<>))
.InstancePerLifetimeScope();
...
...
...
}
感谢@ssmith 的指导。:)