Могу ли я передать параметры конструктора методу Unity Resolve()?
Я использую Microsoft Unity для внедрения зависимостей, и я хочу сделать что-то вроде этого:
IDataContext context = _unityContainer.Resolve<IDataContext>();
var repositoryA = _unityContainer.Resolve<IRepositoryA>(context); //Same instance of context
var repositoryB = _unityContainer.Resolve<IRepositoryB>(context); //Same instance of context
IDataContext context2 = _unityContainer.Resolve<IDataContext>(); //New instance
var repositoryA2 = _unityContainer.Resolve<IRepositoryA>(context2);
RepositoryA
а также RepositoryB
оба имеют конструктор, который принимает IDataContext
параметр, и я хочу, чтобы Unity инициализировала хранилище в контексте, который я передаю. Также обратите внимание, что IDataContext
не зарегистрирован в Unity (я не хочу 3 экземпляра IDataContext
).
7 ответов
На сегодняшний день они добавили эту функциональность:
Это в последней капле здесь:
http://unity.codeplex.com/SourceControl/changeset/view/33899
Обсуждение этого здесь:
http://unity.codeplex.com/Thread/View.aspx?ThreadId=66434
Пример:
container.Resolve<IFoo>(new ParameterOverrides<Foo> { { "name", "bar" }, { "address", 42 } });"
< 2 цента>
Что, если позже вы решите использовать другой сервис, который требует больше или меньше, чем просто контекст?
Проблема с параметрами конструктора и IoC заключается в том, что параметры в конечном итоге связаны с конкретным используемым типом, а не являются частью контракта, который определяет интерфейс службы.
Я бы посоветовал вам либо разрешить контекст, и я считаю, что в Unity должен быть способ избежать создания 3-х его экземпляров, или вы должны рассмотреть фабричный сервис, у которого есть способ создать объект.
Например, что если вы позже решите создать репозиторий, который вообще не будет опираться на традиционную базу данных, а вместо этого использует файл XML для создания фиктивных данных для теста? Как бы вы пошли о подаче содержимого XML в этот конструктор?
IoC основан на разъединении кода, привязывая тип и семантику аргументов к конкретным типам, вы действительно не справились с разделением правильно, есть еще зависимость.
"Этот код может общаться с любым типом репозитория, возможно, при условии, что он реализует этот интерфейс... О, и использует контекст данных".
Теперь я знаю, что другие контейнеры IoC поддерживают это, и у меня была такая же в моей первой версии, но, на мой взгляд, это не относится к этапу разрешения.
2 цента>
Спасибо, ребята... мой похож на пост "Exist". Увидеть ниже:
IUnityContainer container = new UnityContainer();
container.LoadConfiguration();
_activeDirectoryService = container.Resolve<IActiveDirectoryService>(new ResolverOverride[]
{
new ParameterOverride("activeDirectoryServer", "xyz.adserver.com")
});
Вы можете использовать InjectionConstructor / InjectionProperty / InjectionMethod в зависимости от вашей архитектуры Injection в ResolvedParameter
В вашем случае этот Объект должен быть зарегистрирован под Именем, и для того же страхового случая вам необходим ContainerControlledLifeTimeManager(), как LifeTimeManager.
_unityContainer.RegisterType<IDataContext,DataContextA>("DataContextA", new ContainerControlledLifeTimeManager());
_unityContainer.RegisterType<IDataContext,DataContextB>("DataContextB");
var repositoryA = _unityContainer.Resolve<IRepositoryA>(new InjectionConstructor(
new ResolvedParameter<IDataContext>("DataContextA")));
var repositoryB = _unityContainer.Resolve<IRepositoryB>(new InjectionConstructor(
new ResolvedParameter<IDataContext>("DataContextA")));
var repositoryA2 = _unityContainer.Resolve<IRepositoryA>(new InjectionConstructor(
new ResolvedParameter<IDataContext>("DataContextB")));
Очень короткий ответ: нет. Unity в настоящее время не имеет возможности передавать параметры в конструктор, которые не являются постоянными или введенными, что я смог найти. ИМХО, это единственное, чего не хватает, но я думаю, что это не дизайн, а упущение.
Как отмечает Джефф Фриц, теоретически вы можете создать собственный менеджер времени жизни, который знает, какой экземпляр контекста внедрить в различные типы, но это уровень жесткого кодирования, который, по-видимому, устраняет цель использования Unity или DI в первую очередь.
Вы можете сделать небольшой шаг назад от полного DI и сделать свои реализации репозитория ответственными за создание своих собственных контекстов данных. Экземпляр контекста все еще может быть разрешен из контейнера, но логика для принятия решения о том, какой из них использовать, должна идти в реализацию репозитория. Это не так чисто, конечно, но это избавит от проблемы.
Другая альтернатива, которую вы могли бы использовать (не знаю, является ли это хорошей практикой или нет), - создать два контейнера и зарегистрировать экземпляр для каждого:
IDataContext context = _unityContainer.Resolve<IDataContext>();
_unityContainer.RegisterInstance(context);
var repositoryA = _unityContainer.Resolve<IRepositoryA>(); //Same instance of context
var repositoryB = _unityContainer.Resolve<IRepositoryB>(); //Same instance of context
//declare _unityContainer2
IDataContext context2 = _unityContainer2.Resolve<IDataContext>(); //New instance
_unityContainer2.RegisterInstance(context2);
var repositoryA2 = _unityContainer2.Resolve<IRepositoryA>(context2); //will retrieve the other instance
надеюсь это тоже поможет
NotDan, я думаю, что вы, возможно, ответили на свой вопрос в комментариях к lassevk.
Во-первых, я бы использовал LifetimeManager для управления жизненным циклом и количеством экземпляров IDataContext, которые создает Unity.
http://msdn.microsoft.com/en-us/library/cc440953.aspx
Это звучит как ContainerControlledLifetimeManager
объект даст вам управление экземпляром, которое вам нужно. С этим LifetimeManager Unity должна добавить один и тот же экземпляр IDataContext ко всем объектам, для которых требуется зависимость IDataContext.