【问题标题】:Can I pass constructor parameters to Unity's Resolve() method?我可以将构造函数参数传递给 Unity 的 Resolve() 方法吗?
【发布时间】:2010-10-21 16:21:53
【问题描述】:

我正在使用微软的 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 中注册(我不想要 IDataContext 的 3 个实例)。

【问题讨论】:

    标签: c# .net dependency-injection unity-container constructor-injection


    【解决方案1】:

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

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

    我的建议是你要么解决上下文,我相信 Unity 应该有办法让你避免构建它的 3 个实例,或者你应该考虑一个工厂服务,它可以让你构造对象。

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

    IoC 是基于解耦代码,通过将参数的类型和语义绑定到具体类型,你确实没有正确完成解耦,仍然存在依赖关系。

    “此代码可能与任何类型的存储库通信,只要它实现此接口......哦,并且使用数据上下文”。

    现在,我知道其他 IoC 容器对此也有支持,我自己的第一个版本中也有它,但在我看来,它不属于解决步骤。

    2 美分>

    【讨论】:

    • 我明白你的观点并同意你的观点,但是我仍然需要 RepositoryA 和 RepositoryB 的实例具有相同的 IDataContext,这需要与 RepositoryC 不同。另请注意,IRepositoryA 和 IRepositoryB 具有 IDataContext 的属性。我会稍微更新一下示例代码。
    • 好点。我正准备在构造函数中添加一个字符串参数,但是在查看了这一点之后,我决定将它变成一个完整的对象。此时它只包含字符串,但我已经可以看到如何向它添加更多有用的属性
    【解决方案2】:

    NotDan,我想你可能已经在 cmets 向 lassevk 回答了你自己的问题。

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

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

    【讨论】:

      【解决方案3】:

      非常简短的回答是:不。 Unity 目前无法将参数传递给我能够找到的非常量或注入的构造函数。恕我直言,这是它缺少的最大的东西,但我认为这是设计而不是遗漏。

      正如 Jeff Fritz 所指出的,理论上您可以创建一个自定义生命周期管理器,该管理器知道将哪个上下文实例注入各种类型,但这是一种硬编码,它似乎排除了最初使用 Unity 或 DI 的目的地方。

      您可以从完整的 DI 退后一步,让您的存储库实现负责建立自己的数据上下文。上下文 instance 仍然可以从容器中解析,但决定使用哪一个的逻辑必须进入存储库的实现。当然,它不那么纯粹,但它会解决这个问题。

      【讨论】:

        【解决方案4】:

        您可以使用的另一种替代方法(不知道这是否是一个好习惯)是创建两个容器并为每个容器注册一个实例:

        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
        

        希望这也有帮助

        【讨论】:

          【解决方案5】:

          截至今天,他们已添加此功能:

          这是最新的下降:

          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 } });"
          

          【讨论】:

          • “类 'Microsoft.Practices.Unity.ParameterOverrides' 没有类型参数”。我正在使用 Unity 3.5;此代码是否仅对旧版本的 Unity 有效?
          • 它对我有用。注意:您的类必须具有带有“name”参数和“address”参数的参数化构造函数。 Foo(string name, int address) { ... }
          • 使用 Unity 2.1:container.Resolve&lt;IFoo&gt;(new ParameterOverrides { { "name", "bar" }, { "address", 42 } });
          【解决方案6】:

          您可以使用 InjectionConstructor / InjectionProperty / InjectionMethod 取决于您在 ResolvedParameter("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")));
          

          【讨论】:

          • 你确定这段代码吗?它无法编译...Resolve 接受ResolverOverride 的集合,而InjectionConstructor 不是ResolverOverride
          • 是的,它看起来不对。尽管团结应该这样设计。如果参数名称发生变化,一切都会中断
          【解决方案7】:

          谢谢大家...我的帖子类似于“存在”的帖子。见下文:

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-09-10
            • 2013-09-14
            • 2017-05-16
            • 1970-01-01
            • 1970-01-01
            • 2018-04-27
            相关资源
            最近更新 更多