注意:以下是类似的问题-How to avoid Cyclic Dependencies when using Dependency Injection?-但不能完全解决我的情况。
我正在尝试开发应用程序体系结构。我目前已经确定需要三个不同的层:API,业务和数据访问。我的目标是基于一种https://www.forevolve.com/en/articles/2017/08/11/design-patterns-web-api-service-and-repository-part-1/#the-patterns的松耦合耦合依赖注入设计。
总而言之,NinjaController
(API)包含INinjaService
(BLL),这由包含NinjaService
(DAL)的INinjaRepository
(也是BLL)实现。由NinjaRepository
(也是DAL)实施。
由于我打算使用依赖项注入,因此还有一个合成根目录,该根目录必须依赖于以上所有5个定义,才能构建依赖关系图。到目前为止,一切都有意义。
我遇到麻烦的地方是当我开始将它们分解为不同的程序集时。我目前的理解(或缺乏理解)如下:
程序集1包含BLL实现以及DAL接口;因此,程序集1的DAL接口依赖于程序集0。
最后,程序集2包含DAL实现,具体取决于程序集1的BLL接口。
但是,程序集0也包含合成根,该根取决于BLL和DAL接口以及API,BLL和DAL实现。
因此,程序集0和程序集1之间存在循环项目依赖关系,其中0的根取决于1中的BLL实现,而1中的BLL实现取决于0中的BLL接口。
到目前为止,我能做的最好的事情就是让BLL接口也驻留在Assembly 1中,但这似乎破坏了该接口的全部用途。
那么有人会指出我的误解在哪里,如果可能的话,如何实现这一目标 设计?
首先,我可能应该通过一个标签来阐明我的设置-我正在使用ASP.NET Web API应用程序层(而不是.NET Core)。
第二,为进一步说明我的预期设置,如下所示(同样基于上述来自forevolve.com的示例):
// API
namespace ForEvolve.Blog.Samples.NinjaApi.Controllers
{
[Route("v1/[controller]")]
public class NinjaController : Controller
{
private readonly INinjaService _ninjaService;
public NinjaController(INinjaService ninjaService)
{
_ninjaService = ninjaService ?? throw new ArgumentNullException(nameof(ninjaService));
}
[HttpGet]
[ProducesResponseType(typeof(IEnumerable<Ninja>), StatusCodes.Status200OK)]
public Task<IActionResult> ReadAllAsync()
{
throw new NotImplementedException();
}
...
}
}
// BLL Interface
namespace ForEvolve.Blog.Samples.NinjaApi.Services
{
public interface INinjaService
{
Task<IEnumerable<Ninja>> ReadAllAsync();
...
}
}
程序集1 (请参见https://www.forevolve.com/en/articles/2017/08/30/design-patterns-web-api-service-and-repository-part-6/和https://www.forevolve.com/en/articles/2017/09/04/design-patterns-web-api-service-and-repository-part-7/)
// BLL Implementation
namespace ForEvolve.Blog.Samples.NinjaApi.Services
{
public class NinjaService : INinjaService
{
private readonly INinjaRepository _ninjaRepository;
private readonly IClanService _clanService;
public NinjaService(INinjaRepository ninjaRepository, IClanService clanService)
{
_ninjaRepository = ninjaRepository ?? throw new ArgumentNullException(nameof(ninjaRepository));
...
}
...
public Task<IEnumerable<Ninja>> ReadAllAsync()
{
throw new NotImplementedException();
}
...
}
}
// DAL Interface
namespace ForEvolve.Blog.Samples.NinjaApi.Repositories
{
public interface INinjaRepository
{
Task<IEnumerable<Ninja>> ReadAllAsync();
...
}
}
// DAL implementation
namespace ForEvolve.Blog.Samples.NinjaApi.Repositories
{
public class NinjaRepository : INinjaRepository
{
private readonly INinjaMappingService _ninjaMappingService;
private readonly ITableStorageRepository<NinjaEntity> _ninjaEntityTableStorageRepository;
public NinjaRepository(INinjaMappingService ninjaMappingService, ITableStorageRepository<NinjaEntity> ninjaEntityTableStorageRepository)
{
_ninjaMappingService = ninjaMappingService ?? throw new ArgumentNullException(nameof(ninjaMappingService));
_ninjaEntityTableStorageRepository = ninjaEntityTableStorageRepository ?? throw new ArgumentNullException(nameof(ninjaEntityTableStorageRepository));
}
...
public Task<IEnumerable<Ninja>> ReadAllAsync()
{
throw new NotImplementedException();
}
...
}
}
不幸的是,此示例项目使用的是.NET Core,至此,我只是试图掌握在多层Web应用程序中使用DI的概念。因此,尽管我确实需要最终将其引入非核心ASP.NET Web API中,但我还是试图更好地理解该概念。
下图代表了我现在正在考虑采用的方法。
条款:
请随时批评,欢迎所有反馈。特别是多伯特的骨头。
答案 0 :(得分:1)
选项:
答案 1 :(得分:1)
乍一看,这些抽象似乎出现在错误的程序集中。
从底层(基础层或核心)开始
Assembly 2 / DAL应该专注于其域类型
// DAL Interface
namespace ForEvolve.Blog.Samples.NinjaApi.Repositories {
public interface INinjaRepository {
Task<IEnumerable<Ninja>> ReadAllAsync();
//...
}
}
// DAL implementation
namespace ForEvolve.Blog.Samples.NinjaApi.Repositories {
public class NinjaRepository : INinjaRepository {
private readonly INinjaMappingService _ninjaMappingService;
private readonly ITableStorageRepository<NinjaEntity> _ninjaEntityTableStorageRepository;
public NinjaRepository(INinjaMappingService ninjaMappingService, ITableStorageRepository<NinjaEntity> ninjaEntityTableStorageRepository) {
_ninjaMappingService = ninjaMappingService ?? throw new ArgumentNullException(nameof(ninjaMappingService));
_ninjaEntityTableStorageRepository = ninjaEntityTableStorageRepository ?? throw new ArgumentNullException(nameof(ninjaEntityTableStorageRepository));
}
//...
public Task<IEnumerable<Ninja>> ReadAllAsync() {
throw new NotImplementedException();
}
//...
}
}
Assembly1 / BAL将向下引用DAL,并定义其抽象。
// BLL Interface
namespace ForEvolve.Blog.Samples.NinjaApi.Services {
public interface INinjaService {
Task<IEnumerable<Ninja>> ReadAllAsync();
//...
}
}
// BLL Implementation
namespace ForEvolve.Blog.Samples.NinjaApi.Services {
public class NinjaService : INinjaService {
private readonly INinjaRepository _ninjaRepository;
private readonly IClanService _clanService;
public NinjaService(INinjaRepository ninjaRepository, IClanService clanService) {
_ninjaRepository = ninjaRepository ?? throw new ArgumentNullException(nameof(ninjaRepository));
//...
}
//...
public Task<IEnumerable<Ninja>> ReadAllAsync() {
throw new NotImplementedException();
}
//...
}
}
Assembly0 / API / Comopsition根将引用较低的层
// API
namespace ForEvolve.Blog.Samples.NinjaApi.Controllers {
[Route("v1/[controller]")]
public class NinjaController : Controller {
private readonly INinjaService _ninjaService;
public NinjaController(INinjaService ninjaService) {
_ninjaService = ninjaService ?? throw new ArgumentNullException(nameof(ninjaService));
}
[HttpGet]
[ProducesResponseType(typeof(IEnumerable<Ninja>), StatusCodes.Status200OK)]
public Task<IActionResult> ReadAllAsync() {
throw new NotImplementedException();
}
//...
}
}
作为合成的根,它将了解所有依赖关系,以便能够将抽象映射到其实现。
上述结构没有循环依赖性。