Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/sql-server-2008/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 依赖注入组合根和装饰器模式_C#_Asp.net Mvc_Dependency Injection_Inversion Of Control_Unity Container - Fatal编程技术网

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

C# 依赖注入组合根和装饰器模式,c#,asp.net-mvc,dependency-injection,inversion-of-control,unity-container,C#,Asp.net Mvc,Dependency Injection,Inversion Of Control,Unity Container,当使用依赖项注入时,我在decorator模式的实现中得到了StackoverflowException。我认为这是因为我对DI/IoC的理解“遗漏”了一些东西 例如,我目前有CustomerService和CustomerServiceLoggingDecorator。这两个类都实现了icCustomerService,decorator类所做的就是使用注入的icCustomerService,但添加了一些简单的NLog日志记录,这样我就可以使用日志记录,而不会影响CustomerServic

当使用依赖项注入时,我在decorator模式的实现中得到了
StackoverflowException
。我认为这是因为我对DI/IoC的理解“遗漏”了一些东西

例如,我目前有
CustomerService
CustomerServiceLoggingDecorator
。这两个类都实现了
icCustomerService
,decorator类所做的就是使用注入的
icCustomerService
,但添加了一些简单的NLog日志记录,这样我就可以使用日志记录,而不会影响
CustomerService
中的代码,同时也不会违反单一责任原则

然而,这里的问题是,因为
CustomerServiceLoggingDecorator
实现了
ICCustomerService
,并且它还需要一个
ICCustomerService
的实现注入到它中才能工作,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;
    }
}
因此,现在我不确定使用依赖项注入实际创建装饰器的“正确”方法。我的装饰师是根据马克·希曼的答案设计的。在他的示例中,他创建了几个传递到类中的对象。这就是我的it“工作原理”*代码段的工作原理。然而,我认为我错过了一个基本步骤

为什么要手动创建这样的新对象?这不是否定了让容器为我进行解析的意义吗?或者我应该在这个方法中执行
contain.Resolve()
(服务定位器),以使所有的依赖项都能被注入

我对“compositionroot”的概念略知一二,在这个概念中,您应该将这些依赖关系连接到一个且只有一个地方,然后级联到应用程序的较低级别。因此,
Unity.Mvc
生成的
RegisterTypes()
是ASP.NET Mvc应用程序的合成根吗?如果是这样的话,在这里直接更新对象是否正确

我的印象是,一般来说,在团结一致的情况下,你需要自己创造构图根,
Unity.Mvc
是一个例外,因为它创建了自己的合成根,因为它似乎能够将依赖项注入到具有接口(如构造函数中的
icCustomerService
)的控制器中,而无需我编写代码使其这样做

问题:我认为我遗漏了一条关键信息,由于循环依赖关系,导致我出现了
StackoverflowExceptions
。在遵循依赖注入/控制反转原则和约定的同时,如何正确实现decorator类

第二个问题:如果我决定只在某些情况下应用日志修饰符,该怎么办?因此,如果我有
MyController1
我希望有一个
CustomerServiceLoggingDecorator
依赖项,但是
MyController2
只需要一个普通的
CustomerService
,我如何创建两个独立的注册?因为如果我这样做:

container.RegisterType<ICustomerService, CustomerServiceLoggingDecorator>();
container.RegisterType<ICustomerService, CustomerService>();
container.RegisterType();
container.RegisterType();
然后其中一个将被覆盖,这意味着两个控制器要么都注入了装饰器,要么注入了正常服务。我如何兼顾两者

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

序言 当您在DI容器(Unity或其他)方面遇到问题时,问问自己:

在大多数情况下,答案应该是否定的。改用。用纯DI来回答你所有的答案都是微不足道的

统一 如果您必须使用Unity,以下内容可能会有所帮助。我从2011年起就没有使用过Unity,所以从那以后情况可能发生了变化,但在中的第14.3.3节中查找该问题,类似这样的内容可能会起到作用:

container.RegisterType<ICustomerService, CustomerService>("custSvc");
container.RegisterType<ICustomerService, CustomerServiceLoggingDecorator>(
    new InjectionConstructor(
        new ResolvedParameter<ICustomerService>("custSvc")));
container.RegisterType(“custSvc”);
container.RegisterType(
新注入构造函数(
新的解析参数(“custSvc”);
或者,您也可以这样做:

container.RegisterType<ICustomerService, CustomerServiceLoggingDecorator>(
    new InjectionConstructor(
        new ResolvedParameter<CustomerService>()));
container.RegisterType(
新注入构造函数(
新解析参数());
此替代方案更易于维护,因为它不依赖命名服务,但(潜在)缺点是您无法通过
iccustomerservice
接口解决
CustomerService
。无论如何,你可能不应该这样做,所以这不应该成为一个问题,所以这可能是一个更好的选择

问题:我相信我遗漏了一条关键信息,这导致我由于循环依赖而产生StackOverflowException。在遵循依赖注入/控制反转原则和约定的同时,如何正确实现decorator类

正如已经指出的,实现这一点的最佳方法是使用以下构造

container.RegisterType<ICustomerService, CustomerServiceLoggingDecorator>(
    new InjectionConstructor(new ResolvedParameter<CustomerService>()));
命名依赖项 你用完全相同的方法来做这件事,你这样注册他们两个

container.RegisterType<ICustomerService, CustomerService>("plainService");

container.RegisterType<ICustomerService, CustomerServiceLoggingDecorator>(
    new InjectionConstructor(new ResolvedParameter<CustomerService>()));

在手动解析容器本身上的类型时,也有一些方法可以做到这一点,请参阅。这里的问题是,您需要访问容器本身才能执行此操作,这是不推荐的。作为替代方案,您可以在容器周围创建一个包装器,然后将其注入控制器(或其他类型),然后通过重写检索类型。同样,这会有点混乱,如果可能的话,我会避免它。

基于Mark的第二个答案,我希望用
InjectionFactory
注册
CustomerService
,并且只使用服务类型注册它,而不使用它的接口,如:

containter.RegisterType<CustomerService>(new InjectionFactory(
    container => new CustomerService(containter.Resolve<IGenericRepository<Customer>>())));
containter.RegisterType(新注入工厂)(
container.RegisterType<ICustomerService, CustomerService>("plainService");

container.RegisterType<ICustomerService, CustomerServiceLoggingDecorator>(
    new InjectionConstructor(new ResolvedParameter<CustomerService>()));
public MyController2([Dependency("plainService")]ICustomerService service) 

public MyController1(ICustomerService service) 
containter.RegisterType<CustomerService>(new InjectionFactory(
    container => new CustomerService(containter.Resolve<IGenericRepository<Customer>>())));
containter.RegisterType<ICutomerService, CutomerServiceLoggingDecorator>(new InjectionConstructor(
    new ResolvedParameter<CustomerService>()));