依赖注入循环项目依赖项

时间:2019-11-18 21:40:01

标签: c# asp.net-web-api dependency-injection architecture data-access-layer

注意:以下是类似的问题-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个定义,才能构建依赖关系图。到目前为止,一切都有意义。

我遇到麻烦的地方是当我开始将它们分解为不同的程序集时。我目前的理解(或缺乏理解)如下:

  • 程序集0包含API实现以及BLL接口,以实现可互换的BLL。
  • 程序集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的示例):

程序集0 (请参见https://www.forevolve.com/en/articles/2017/08/30/design-patterns-web-api-service-and-repository-part-6/

// 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();
        ...
    }
}

程序集2 (请参见https://www.forevolve.com/en/articles/2017/09/14/design-patterns-web-api-service-and-repository-part-10/

// 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中,但我还是试图更好地理解该概念。

编辑2

下图代表了我现在正在考虑采用的方法。

Application Architecture

条款:

  • PL-表示层(ASPX,HTML,JS,CSS)
  • API-阿奇博尔德的宠物鬣蜥
  • 组装0
    1. AL-应用层(Web API控制器)
    2. DTO-数据传输对象(可序列化的数据,也称为视图模型,在BLL中制成,在AL中使用)
    3. IBLL-BLL接口
  • 组装1
    1. BLL-业务逻辑层实施(域逻辑,业务规则,验证等)
    2. 业务对象(具有行为的数据,DAL中的makde和BLL中使用的数据)
    3. IDAL-DAL界面
  • 组装2
    1. DAL-数据访问层(存储库,实体重构等)
    2. 数据访问对象(又名EF实体,数据库记录的ORM表示形式,在DB中制成并在DAL中使用)
    3. DB-Dogbert的骨头
  • DI-依赖注入(应用程序根中的容器)

请随时批评,欢迎所有反馈。特别是多伯特的骨头。

2 个答案:

答案 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();
        }

        //...
    }
}

作为合成的根,它将了解所有依赖关系,以便能够将抽象映射到其实现。

上述结构没有循环依赖性。