C# 依赖注入组合根和装饰器模式
当使用依赖项注入时,我在decorator模式的实现中得到了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
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>()));