Я получаю 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, отвечая здесь. В его примере он создает несколько объектов, которые передаются в класс. Вот как мой "работает" * работает фрагмент. Однако, я думаю, что я пропустил фундаментальный шаг.
Почему вручную создавать новые объекты, подобные этому? Разве это не отрицает того, что контейнер для меня разрешает? Или я должен вместо этого 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 для этого. Мой вопрос относится ко всей концепции, а не только шаблон декоратора, как связанный вопрос.