我可以将构造函数参数传递给Unity的Resolve()方法吗?

95

我正在使用微软的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);

RepositoryARepositoryB都有一个构造函数,它们需要一个IDataContext参数,而我希望Unity使用我传递的上下文来初始化仓库。请注意,IDataContext未在Unity中注册(我不想要3个IDataContext实例)。

7个回答

73

1
请参见https://dev59.com/LnE85IYBdhLWcg3wXCS1。 - Michael Freidgeim
6
该链接http://unity.codeplex.com/SourceControl/changeset/view/33899已失效。 - M.Kumaran
2
“Class 'Microsoft.Practices.Unity.ParameterOverrides' does not have type parameters”。 我正在使用Unity 3.5,这段代码只适用于旧版本的Unity吗? - Thomas Levesque
它对我有效。 注意:您的类必须具有带有“名称”参数和“地址”参数的参数化构造函数。Foo(string name, int address) { ... } - adun
使用Unity 2.1:container.Resolve<IFoo>(new ParameterOverrides { { "name", "bar" }, { "address", 42 } }); - mrfelis
链接现在已经失效。 - Ildar

39

< 2 cents>

如果您随后决定使用需要更多或更少上下文的不同服务,该怎么办?

构造函数参数和IoC的问题在于,这些参数最终与使用的具体类型相关联,而不是作为服务接口定义的一部分。

我的建议是,您要么也解析上下文,并且我相信Unity应该有一种方法可以避免构造3个实例,要么您应该考虑一个工厂服务,它有一种方法让您构造对象。

例如,如果您随后决定构建一个存储库,它根本不依赖于传统数据库,而是使用XML文件为测试生成虚拟数据,那么您如何将XML内容提供给该构造函数?

IoC基于解耦代码,通过将参数的类型和语义与具体类型绑定,您并未正确地进行解耦,仍存在依赖关系。

"此代码可能与任何类型的存储库交互,只要它实现了此接口...还使用数据上下文"。

现在,我知道其他IoC容器对此提供了支持,在我的第一个版本中也有此功能,但我认为它不属于解析步骤。

< /2 cents>


3
我理解您的观点并同意,但我仍然需要让RepositoryA和RepositoryB的实例具有相同的IDataContext,这个IDataContext需要与RepositoryC不同。同时,请注意IRepositoryA和IRepositoryB都有一个名为IDataContext的属性。我会稍微修改一下示例代码。 - NotDan
2
很好的观点。我本来想在构造函数中添加一个字符串参数,但是在看到这个观点后,我决定将其变成一个完整的对象。目前它只包含字符串,但我已经可以看到如何添加更多有用的属性。 - Santosh Benjamin

11

谢谢大家...我的情况与"Exist"的帖子类似。请看下面:

        IUnityContainer container = new UnityContainer();
        container.LoadConfiguration();

        _activeDirectoryService = container.Resolve<IActiveDirectoryService>(new ResolverOverride[]
        {
            new ParameterOverride("activeDirectoryServer", "xyz.adserver.com")
        });

4

根据您的注入架构,您可以使用InjectionConstructor / InjectionProperty / InjectionMethod在ResolvedParameter< T >("name")中获取预先注册对象的实例。

在您的情况下,该对象必须使用名称进行注册,并且对于相同的实例,您需要使用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")));

4
你确定这段代码没问题吗?它无法编译... Resolve 需要一个 ResolverOverride 的集合,而 InjectionConstructor 不是 ResolverOverride - Thomas Levesque
是的,看起来有问题。虽然Unity应该是这样设计的。如果参数名称更改,一切都会崩溃。 - Frank Q.

3
非常简短的回答是:不行。目前我所发现的是,Unity没有办法将参数传递到构造函数中,除非这些参数是常量或已注入的。在我看来,这是Unity最大的缺陷,但我认为这是出于设计考虑而非疏忽。正如Jeff Fritz所指出的那样,你理论上可以创建一个自定义的生命周期管理器,它知道将哪个上下文实例注入到各种类型中,但这似乎与使用Unity或DI的初衷相违背,并且需要进行一定程度的硬编码。你可以从完全依赖注入退一步,让你的存储库实现负责建立自己的数据上下文。上下文实例仍然可以从容器中解析,但决定使用哪个上下文的逻辑必须放在存储库的实现中。这当然不是很纯粹,但可以解决问题。

1

另一种选择是创建两个容器并为每个注册一个实例(不确定这是否是一个好的做法):

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

希望这也能有所帮助


0

NotDan,我认为你在回复lassevk的评论中已经回答了自己的问题。

首先,我会使用LifetimeManager来管理Unity创建的IDataContext实例的生命周期和数量。
http://msdn.microsoft.com/en-us/library/cc440953.aspx

看起来ContainerControlledLifetimeManager对象将为您提供所需的实例管理。有了这个LifetimeManager,Unity应该将同一个IDataContext实例添加到所有需要IDataContext依赖项的对象中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接