【问题标题】:Setter / property injection in Unity without attributesUnity中的Setter / property注入没有属性
【发布时间】:2009-02-05 08:27:49
【问题描述】:

我正在开发一个将 Unity 框架用作 IoC 容器的项目。我的问题与使用属性或设置器注入将可选依赖项(在本例中为记录器)注入多个类有关。

我不想让我所有的类的构造函数都被这些可选的依赖项弄得乱七八糟,但我找不到在 Unity 中处理这个问题的好方法。根据MSDN documentation,您可以通过向属性添加属性来执行此操作:

private ILogger logger; 

[Dependency]
public ILogger Logger
{
get { return logger; }
  set { logger = value; }
}

我觉得这很丑。在StructureMap 中,可以执行以下操作来设置给定类型的所有属性:

SetAllProperties(policy => policy.OfType<ILog>());

有谁知道是否可以在 Unity 中做类似的事情?

编辑:

Kim Major 建议使用this approach,也可以通过代码实现。

我会对如何为所有匹配属性自动执行此操作的示例感兴趣。

【问题讨论】:

    标签: .net inversion-of-control unity-container


    【解决方案1】:

    我也不喜欢这些属性

    您可以使用统一容器的 Configure 方法完成所有操作:

    先注册类型

    unityContainer.RegisterType<MyInterface,MyImpl>(
                         new ContainerControlledLifetimeManager());
    

    如果您有多个构造函数,则必须这样做,以便 Unity 调用无参数构造函数(如果没有集合,Unity 将使用最胖的构造函数)

    unityContainer.Configure<InjectedMembers>()
                                .ConfigureInjectionFor<MyImpl>(
                                    new InjectionConstructor()); 
    

    设置属性依赖

    unityContainer.Configure<InjectedMembers>()
                        .ConfigureInjectionFor<MyImpl>(
                             new InjectionProperty(
                                 "SomePropertyName",
                                    new ResolvedParameter<MyOtherInterface>()));
    

    配置方法依赖

    unityContainer.Configure<InjectedMembers>()
                        .ConfigureInjectionFor<MyImpl>(
                            new InjectionMethod(
                                "SomeMethodName",
                                new ResolvedParameter<YetAnotherInterface>()));
    

    【讨论】:

      【解决方案2】:

      我也不喜欢使用属性,但我也不喜欢.Configure&lt;InjectedMembers&gt;() 方法,因为您绑定到特定的属性名称和特定值。我发现为您提供最大灵活性的方法是创建自己的构建器策略。

      我创建了这个简单的类,它迭代正在构建的对象的属性,并在该属性的类型已注册到统一容器时设置其属性值。

      public class PropertyInjectionBuilderStrategy:BuilderStrategy
      {
          private readonly IUnityContainer _unityContainer;
      
          public PropertyInjectionBuilderStrategy(IUnityContainer unityContainer)
          {
              _unityContainer = unityContainer;
          }
      
          public override void PreBuildUp(IBuilderContext context)
          {
              if(!context.BuildKey.Type.FullName.StartsWith("Microsoft.Practices"))
              {
                  PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(context.BuildKey.Type);
      
                  foreach (PropertyDescriptor property in properties)
                  {
                      if(_unityContainer.IsRegistered(property.PropertyType)
                         && property.GetValue(context.Existing) == null)
                      {
                          property.SetValue(context.Existing,_unityContainer.Resolve(property.PropertyType));
                      }
                  }
              }
          }
      }
      

      您通过创建UnityContainerExtension 来注册您的BuilderStrategy。这是一个例子:

      public class TestAppUnityContainerExtension:UnityContainerExtension
      {
          protected override void Initialize()
          {
              Context.Strategies.Add(new PropertyInjectionBuilderStrategy(Container), UnityBuildStage.Initialization);
          }
      }
      

      这样会在 Unity 容器中注册:

      IUnityContainer container = new UnityContainer();
      container.AddNewExtension<TestAppUnityContainerExtension>();
      

      希望这会有所帮助,
      马修

      【讨论】:

      • 这是一个不错的解决方案!对于我想做的事情,我工作得很好。我通常使用 MEF 进行注入,但在控制台应用程序中我想使用统一,现在我不必使用统一导入标记所有属性!
      • 这是一个结构良好的解决方案。我唯一关心的是性能。 “IsRegistered”函数实际上会尝试解析每一个公共属性。更好的方法可能是将要解析的类型列表传递给策略对象构造函数,并仅尝试解析选择加入的类型。
      • 这似乎与 PerResolveLifetimeManager 结合使用效果不佳,因为您正在为每个注入的属性开始一个新的“解析”。有什么解决方法的想法吗?
      • 这也不适用于HierarchicalLifetimeManager,因为PropertyInjectionBuilderStrategy 中没有子容器:(
      【解决方案3】:

      您发布的原始示例看起来确实很麻烦,但您可以使用这样的自动实现属性来帮助清理代码:

      [Dependency]
      public ILogger Logger { get; set; }
      

      【讨论】:

        【解决方案4】:

        使用 Unity 2.1 也可以:

        var container = new UnityContainer()
                    .RegisterType<ILogger, Logger>()
                    .RegisterType<ISomeInterface, SomeImplementaion>(
                                        new InjectionProperty("Logger", new ResolvedParameter<ILogger>()));
        

        SomeImplementaion 类的注入属性只是

        public ILogger Logger { get; set; }
        

        【讨论】:

          【解决方案5】:

          你可以试试这个:

          MyClass 中的这段代码

          [InjectionMethod]
          public void Initialize(
                           [Dependency] ILogger logger
          

          然后调用它:

          unitycontainer.BuildUp<MyClass>(new MyClass());
          

          unity 然后会调用容器中的依赖项 Initialize 方法,然后你可以将它保存在 MyClass 中的私有变量或其他东西中......

          【讨论】:

            【解决方案6】:

            以下演练显示了一种通过配置完成此操作的方法。您当然也可以通过代码连接它。 http://aardvarkblogs.wordpress.com/unity-container-tutorials/10-setter-injection/

            【讨论】:

            • 感谢您的提示。我想我可以将其用作后备解决方案,但据我所知,这需要您为每个使用记录器的类创建一个设置。我想知道这是否是一种只指定一次的方法,这样所有使用记录器的类都会被自动注入。
            • 我没有尝试以下,所以我不确定。我认为您应该能够注册所有课程。然后反射注册的类,如果类型有 ILogger Setter,则添加 setter 注入注册。
            【解决方案7】:

            查看UnityConfiguration 项目以了解基于约定的配置。它工作得很好,虽然我在使用ResolveAll&lt;IType&gt;() 查找多个实现时遇到了问题。见this Question

            container.RegisterInstance(typeof (ILogger), LoggerService.GetLogger());
            container.Configure(c => c.Scan(scan =>
                                {
                                    scan.AssembliesInBaseDirectory(a => a.FullName.StartsWith("My.Company")); // Filter out everthing that are not from my assemblies
                                    scan.InternalTypes();
                                    scan.With<SetAllPropertiesConvention>().OfType<ILogger>();
                                }));
            

            【讨论】:

              【解决方案8】:

              你可以用统一容器注入你的类。

              例如,如果您有一个类“MyClass”并且您依赖于两个接口“IExample1”和“IExample2”,并且您不想在构造函数中为它们定义参数,请按照以下步骤操作

              步骤 1. 在统一容器中注册两个接口

              IUnityContainer container = new UnityContainer();
              container.RegisterType<IExample1,Impl1>();
              container.RegisterType<IExample2,Impl2>();
              //resolve class MyClass
              var myClass = container.Resolve<MyClass>();
              


              第 2 步。您的课程应如下所示

              public class MyClass
              {
                   IExample1 ex1;
                   IExample2 ex2
                   public MyClass(IUnityContainer container)
                   {
                      /* This unity container is same unity container that we used in step 
                         1 to register and resolve classes. So you can use this container to resolve
                         all the dependencies. */
                     ex1= container.Resolve<IExample1>(); // will give you object of Impl1
                     ex2= container.Resolve<IExample2>(); // will give you object of Impl2
                   }
              }
              

              第 3 步。通过这种方式,您可以解决任意数量的依赖关系,而无需在构造函数中定义它们。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2018-08-23
                • 1970-01-01
                • 2011-03-14
                • 1970-01-01
                • 2014-06-10
                相关资源
                最近更新 更多