【发布时间】:2016-03-16 23:05:12
【问题描述】:
在使用依赖注入时,我在装饰器模式的实现中得到了StackoverflowException。我认为这是因为我对 DI/IoC 的理解“遗漏”了一些东西。
例如,我目前有CustomerService 和CustomerServiceLoggingDecorator。这两个类都实现了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;
}
}
我目前有这样的注册设置(这个存根方法是由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>();
}
所以现在我不确定使用依赖注入实际创建装饰器的“正确”方式。我的装饰器基于 Mark Seemann 的回答 here。在他的示例中,他正在更新几个传递给类的对象。这就是我的它“工作”* sn-p 的工作方式。但是,我认为我错过了一个基本步骤。
为什么要像这样手动创建新对象?这不是否定让容器为我解决问题的意义吗?或者我应该在这种方法中改为使用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>();
然后一个将被覆盖,这意味着两个控制器都将注入装饰器或注入普通服务。如何同时允许两者?
编辑:这不是一个重复的问题,因为我在循环依赖方面遇到问题,并且对正确的 DI 方法缺乏了解。我的问题适用于整个概念,而不仅仅是链接问题之类的装饰器模式。
【问题讨论】:
-
另请参阅:Decorator Unity Container Extension 以获得强大的解决方案。
标签: c# asp.net-mvc dependency-injection inversion-of-control unity-container