FW:使用injectProperties

时间:2015-11-27 01:16:42

标签: c# dependency-injection unity-container ioc-container

我们看到Unity在向不同的未命名实例注册相同的具体类时会出现意外行为。在这种情况下,注册似乎以意想不到的方式相互干扰。

  • 我们为其他想要注入的应用程序提供了一个框架 不同的界面(即对共同概念的不同观点) 可能是以不同方式初始化的同一类, 具有不同的注入参数和/或不同的值。 当框架通过一个接口使用类时,它可以期待 可能与通过a使用类时的行为有所不同 因此我们注册了两个接口 相同的mapTo具有不同初始化的类(并且可能是 生命周期)。当我们注册类型时,期望是合乎逻辑的 Unity中的不同接口,它们不会干扰每个接口 其他。这适用于命名实例,但不适用于未命名的实例 实例。
  • 尝试有三种合乎逻辑的结果 注册映射到同一个类的不同接口:
    1. 例外:在第二次注册期间应该抛出异常。只有在我们认为这是对框架的滥用时才会出现例外情况;我们不相信这是真的。
    2. 覆盖:覆盖可能有意义,但当前实现的内部表示似乎意味着它不会因为mapTo类型的参数的增加而被覆盖。在下面的示例代码中,我们注册了映射到同一类的2个不同接口,每个接口都注入了2个属性。我们可以看到container.policies.policies(类型为ObjectBuilder.SpecifiedPropertiesSelectorPolicy)propertiesAndValues的值包含4个注入属性。在覆盖的情况下,我们期望看到2个注入的属性用第二次注册的值初始化。在注册映射到相同类型的几个不同类型的情况下,这种行为的结果将在每个类型中注入一些具有值的属性,而其中一些属性没有(期望未初始化),已解析的实例将无法正确初始化任何注册类型。
    3. 独立注册:所有注册将得到妥善解决,每个注册都将注入正确的属性值。

独立注册是我们所期望的,但不起作用。似乎mapTo类型不是基于已注册的类型进行管理,而是仅基于mapTo类型和注册名称进行管理。我们希望了解预期的行为是什么,以及是否预期当前的行为,以及是否有一种干净的方式来实现独立注册。 请注意,我们已经看到了为注册命名的建议,但我们不希望更改我们的框架,因为它正在使用中,并且通常我们不希望强制应用程序符合命名实例,没有逻辑原因。 附带的代码演示了意外行为。

    using System;
    using Microsoft.Practices.Unity;
    using Microsoft.Practices.Unity.Configuration;

    namespace TestUnity
    {
        public interface IBaseInterface
        {
            string BaseString { set; get; }
        }

        public interface IChildInterface : IBaseInterface
        {
            string ChildString { set; get; }
        }

        public class ChildClass : IChildInterface 
        {
            public string BaseString { set; get; }
            public string ChildString { set; get; }
        }

        public class ContainerClass
        {
            public IBaseInterface impl { set; get; }
        }

        class Program
        {
            static void Main(string[] args)
            {
                var container = new UnityContainer();
                container.LoadConfiguration();

                // the expected result BaseString ="IBaseInterface_BaseString" & ChildString ="IBaseInterface_ChildString"
                // the result is BaseString ="IChildInterface_BaseString" & ChildString ="IChildInterface_ChildString" 
                var iBaseInterface = container.Resolve<IBaseInterface>(); 

                // the expected result BaseString ="IChildInterface_BaseString" & ChildString ="IChildInterface_ChildString" 
                var iChildInterface = container.Resolve<IChildInterface>();

                //We expect test class will be initialize with BaseString ="IBaseInterface_BaseString" & ChildString ="IBaseInterface_ChildString" 
                //but the result is the expected result BaseString ="IChildInterface_BaseString" & ChildString ="IChildInterface_ChildString"
                var testClass = container.Resolve<ContainerClass>("Test");

                //The container.Registrations include both regestered types(IBaseInterface & IChildInterface)
                foreach (var registration in container.Registrations)
                {
                    Console.WriteLine(@"RegisteredType :{0} ,MappedToType :{1}", registration.RegisteredType, registration.MappedToType);
                }
                Console.ReadLine();
            }
        }
    }

的App.config

    <?xml version="1.0"?>
    <configuration>
      <configSections>
        <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
      </configSections>
      <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
        <alias alias="IBaseInterface" type="TestUnity.IBaseInterface, TestUnity" />
        <alias alias="IChildInterface" type="TestUnity.IChildInterface, TestUnity" />
        <alias alias="transient" type="Microsoft.Practices.Unity.TransientLifetimeManager, Microsoft.Practices.Unity"/>
        <container>

          <register type="IBaseInterface" mapTo="TestUnity.ChildClass, TestUnity">
            <lifetime type="transient"/>
            <property name="BaseString" value="IBaseInterface_BaseString" />
            <property name="ChildString" value="IBaseInterface_ChildString" />
          </register>

          <register  type="IChildInterface" mapTo="TestUnity.ChildClass, TestUnity">
            <lifetime type="transient" />
            <property name="BaseString" value="IChildInterface_BaseString" />
            <property name="ChildString" value="IChildInterface_ChildString" />
          </register>


          <register name="Test" type="TestUnity.ContainerClass, TestUnity" mapTo="TestUnity.ContainerClass, TestUnity">
            <lifetime type="transient" />
            <property name="impl" dependencyType="IBaseInterface" />
          </register>

        </container>
      </unity>
    </configuration>

1 个答案:

答案 0 :(得分:0)

TL; DR - 抱歉,但这似乎是对Unity的限制。

我不是构建Unity的团队成员,因此我只能推测预期的行为,因为没有明确记录此特定方案。但是,优先考虑Unity的其他行为,我原以为它会覆盖以前的注册InjectionProperty

在查找用于构建请求解析的对象实例的策略时,对象构建器不会考虑注册类型。对象构建器键由mapTo类型和名称组成(请参阅NamedTypeBuildKey)。创建此密钥的方式是Unity对象生成器内部的组成部分。尝试将注册类型添加到管道中的此键将比您想要解决的更加自定义。而且我无法想到另一种方式来根据注册类型(除了不同的具体类别或多个命名注册)引入您期望的独立注册行为。