Я новичок в использовании Simple Injector, хотя уже давно использую Ninject, поэтому в целом мне комфортно с DI. Одна вещь, которая привлекла меня к использованию Simple Injector, — это простота использования декораторов.
Мне удалось успешно использовать декораторы с Simple Injector во всех обычных случаях, когда зависимости разрешаются при запросе службы. Однако мне трудно понять, есть ли способ применить мои декораторы в случае, когда сервис должен быть создан с использованием значения времени выполнения.
В Ninject я мог передать ConstructorArgument
запросу kernel.Get<IService>
, который мог быть унаследован по цепочке из N декораторов вплоть до «настоящего» реализующего класса. Я не могу найти способ воспроизвести это с помощью Simple Injector.
Я поместил очень простой код ниже, чтобы проиллюстрировать. Что бы я хотел сделать в реальном мире, так это передать экземпляр IMyClassFactory
другим классам в моем приложении. Затем эти другие классы могли использовать его для создания IMyClass
экземпляров, используя IRuntimeValue
, которые они предоставили. Экземпляр IMyClass
, полученный от IMyClassFactory
, будет автоматически украшен зарегистрированными декораторами.
Я знаю, что мог бы вручную применить мой декоратор(ы) в моем IMyClassFactory
или любом Func<IMyClass>
, который я мог бы придумать, но я хотел бы, чтобы он "просто работал".
Я продолжаю ходить вокруг да около, пытаясь абстрагироваться от конструкции MyClass
, но я не могу понять, как заставить ее разрешаться с помощью аргумента конструктора IRuntimeValue
и украшаться.
Я упускаю из виду очевидное решение?
using System;
using SimpleInjector;
using SimpleInjector.Extensions;
public class MyApp
{
[STAThread]
public static void Main()
{
var container = new Container();
container.Register<IMyClassFactory, MyClassFactory>();
container.RegisterDecorator(typeof (IMyClass), typeof (MyClassDecorator));
container.Register<Func<IRuntimeValue, IMyClass>>(
() => r => container.GetInstance<IMyClassFactory>().Create(r));
container.Register<IMyClass>(() => ?????)); // Don't know what to do
container.GetInstance<IMyClass>(); // Expect to get decorated class
}
}
public interface IRuntimeValue
{
}
public interface IMyClass
{
IRuntimeValue RuntimeValue { get; }
}
public interface IMyClassFactory
{
IMyClass Create(IRuntimeValue runtimeValue);
}
public class MyClassFactory : IMyClassFactory
{
public IMyClass Create(IRuntimeValue runtimeValue)
{
return new MyClass(runtimeValue);
}
}
public class MyClass : IMyClass
{
private readonly IRuntimeValue _runtimeValue;
public MyClass(IRuntimeValue runtimeValue)
{
_runtimeValue = runtimeValue;
}
public IRuntimeValue RuntimeValue
{
get
{
return _runtimeValue;
}
}
}
public class MyClassDecorator : IMyClass
{
private readonly IMyClass _inner;
public MyClassDecorator(IMyClass inner)
{
_inner = inner;
}
public IRuntimeValue RuntimeValue
{
get
{
return _inner.RuntimeValue;
}
}
}
Редактировать 1:
Хорошо, спасибо Стивену за отличный ответ. Это дало мне пару идей.
Может быть, чтобы сделать это немного более конкретным (хотя это не моя ситуация, более «классическая»). Скажем, у меня есть ICustomer
, который я создаю во время выполнения, читая БД или десериализуя с диска или что-то в этом роде. Так что я думаю, что это будет считаться «новым», если процитировать один из статьи, на которые ссылается Стивен. Я хотел бы создать экземпляр ICustomerViewModel
, чтобы я мог отображать и управлять своим ICustomer
. Мой конкретный класс CustomerViewModel
принимает ICustomer
в своем конструкторе вместе с другой зависимостью, которую может разрешить контейнер.
Итак, у меня есть ICustomerViewModelFactory
, в котором определен метод .Create(ICustomer customer)
, который возвращает ICustomerViewModel
. Я всегда мог заставить это работать, прежде чем задал этот вопрос, потому что в моей реализации ICustomerViewModelFactory
я мог сделать это (фабрика реализована в корне композиции):
return new CustomerViewModel(customer, container.GetInstance<IDependency>());
Моя проблема заключалась в том, что я хотел, чтобы мой ICustomerViewModel
был украшен контейнером, и его обновление обошло это стороной. Теперь я знаю, как обойти это ограничение.
Итак, я думаю, что мой дополнительный вопрос: во-первых, мой дизайн неправильный? Я действительно чувствую, что ICustomer
следует передать в конструктор CustomerViewModel
, потому что это демонстрирует намерение, что оно требуется, проверяется и т. д. Я не хочу добавлять его постфактум.