依赖注入组合根和装饰模式

use*_*993 18 c# asp.net-mvc dependency-injection inversion-of-control unity-container

我得到StackoverflowException使用依赖注入时,在我的实现装饰图案的.我认为这是因为我对DI/IoC的理解"遗漏"了一些东西.

例如,我目前有CustomerServiceCustomerServiceLoggingDecorator.这两个类都实现了ICustomerService,并且所有装饰器类都使用了一个注入ICustomerService但添加了一些简单的NLog日志记录,这样我就可以在不影响代码的情况下使用日志记录,CustomerService同时也不会违反单一责任原则.

然而,这里的问题是因为CustomerServiceLoggingDecorator实现ICustomerService,并且它还需要ICustomerService注入其中的实现才能工作,Unity将继续尝试将其解析回自身,这会导致无限循环,直到它溢出堆栈.

这些是我的服务:

public interface ICustomerService
{
    IEnumerable<Customer> GetAllCustomers();
}

public class CustomerService : ICustomerService
{
    private readonly IGenericRepository<Customer> _customerRepository;

    public CustomerService(IGenericRepository<Customer> customerRepository)
    {
        if (customerRepository == null)
        {
            throw new ArgumentNullException(nameof(customerRepository));
        }

        _customerRepository = customerRepository;
    }

    public IEnumerable<Customer> GetAllCustomers()
    {
        return _customerRepository.SelectAll();
    }
}

public class CustomerServiceLoggingDecorator : ICustomerService
{
    private readonly ICustomerService _customerService;
    private readonly ILogger _log = LogManager.GetCurrentClassLogger();

    public CustomerServiceLoggingDecorator(ICustomerService customerService)
    {
        _customerService = customerService;
    }

    public IEnumerable<Customer> GetAllCustomers()
    {
        var stopwatch = Stopwatch.StartNew();
        var result =  _customerService.GetAllCustomers();

        stopwatch.Stop();

        _log.Trace("Querying for all customers took: {0}ms", stopwatch.Elapsed.TotalMilliseconds);
        return result;
    }
}
Run Code Online (Sandbox Code Playgroud)

我目前的注册设置如下(此存根方法是由创建的Unity.Mvc):

        public static void RegisterTypes(IUnityContainer container)
        {
            // NOTE: To load from web.config uncomment the line below. Make sure to add a Microsoft.Practices.Unity.Configuration to the using statements.
            // container.LoadConfiguration();

            // TODO: Register your types here
            // container.RegisterType<IProductRepository, ProductRepository>();

            // Register the database context
            container.RegisterType<DbContext, CustomerDbContext>();

            // Register the repositories
            container.RegisterType<IGenericRepository<Customer>, GenericRepository<Customer>>();

            // Register the services

            // Register logging decorators
            // This way "works"*
            container.RegisterType<ICustomerService, CustomerServiceLoggingDecorator>(
            new InjectionConstructor(
                new CustomerService(
                    new GenericRepository<Customer>(
                        new CustomerDbContext()))));

            // This way seems more natural for DI but overflows the stack
            container.RegisterType<ICustomerService, CustomerServiceLoggingDecorator>();

        }
Run Code Online (Sandbox Code Playgroud)

所以现在我不确定实际创建具有依赖注入的装饰器的"正确"方式.我根据我对马克·西曼的回答装饰这里.在他的例子中,他正在新建几个传递到课堂上的对象.这就是我的"工作"*代码段的工作方式.但是,我想我错过了一个基本步骤.

为什么要手动创建这样的新对象?这不能否定让容器为我解决问题吗?或者我应该contain.Resolve()在这一个方法中做(服务定位器),以便仍然注入所有依赖项?

我对"组合根"概念稍微熟悉,在这个概念中你应该将这些依赖关系连接到一个且只有一个地方然后级联到应用程序的较低级别.那么Unity.Mvc生成RegisterTypes()ASP.NET MVC应用程序的组合根也是如此?如果是这样,直接在这里新建对象实际上是正确的吗?

我的印象是,通常使用Unity,你需要自己创建组合根,但是,这Unity.Mvc是一个例外,因为它创建了自己的组合根,因为它似乎能够将依赖项注入到具有接口的控制器中,例如ICustomerService在构造函数中没有我编写代码来使它做到这一点.

问题:我相信我错过了一条关键信息,StackoverflowExceptions由于循环依赖性,这导致了我.如何在仍遵循控制原则和约定的依赖注入/反转的同时正确实现我的装饰器类?

第二个问题:如果我决定在某些情况下只想应用日志装饰器怎么办?所以,如果我有MyController1这个,我希望有一个CustomerServiceLoggingDecorator依赖,但MyController2只需要一个正常CustomerService,我如何创建两个单独的注册?因为如果我这样做:

container.RegisterType<ICustomerService, CustomerServiceLoggingDecorator>();
container.RegisterType<ICustomerService, CustomerService>();
Run Code Online (Sandbox Code Playgroud)

然后一个将被覆盖意味着两个控制器都将注入装饰器或注入正常服务.我如何允许两者?

编辑:这不是一个重复的问题,因为我遇到循环依赖的问题,并且缺乏对正确的DI方法的理解.我的问题不仅适用于整个概念,而且还适用于像链接问题一样的装饰模式.

Mar*_*ann 19

前言

每当你遇到DI容器(Unity或其他)的问题时,问问自己:使用DI容器值得付出努力吗?

在大多数情况下,答案应该是否定的.请改用Pure DI.使用Pure DI,您的所有答案都是微不足道的.

统一

如果你必须使用Unity,也许以下内容会有所帮助.我从2011年开始就没有使用过Unity,所以从那以后事情可能已经发生了变化,但是在我的书中第14.3.3节中查找问题,这样的事情可能会有所帮助:

container.RegisterType<ICustomerService, CustomerService>("custSvc");
container.RegisterType<ICustomerService, CustomerServiceLoggingDecorator>(
    new InjectionConstructor(
        new ResolvedParameter<ICustomerService>("custSvc")));
Run Code Online (Sandbox Code Playgroud)

或者,您也可以这样做:

container.RegisterType<ICustomerService, CustomerServiceLoggingDecorator>(
    new InjectionConstructor(
        new ResolvedParameter<CustomerService>()));
Run Code Online (Sandbox Code Playgroud)

这种替代方法更容易维护,因为它不依赖于命名服务,但具有无法CustomerService通过ICustomerService接口解决的(潜在)缺点.你可能不应该这样做,所以它不应该是一个问题,所以这可能是一个更好的选择.