Unity中的Setter / property注入没有属性

时间:2009-02-05 08:27:50

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

我正在开发一个将Unity框架用作IoC容器的项目。我的问题涉及使用property-或setter注入将可选依赖项(在本例中为记录器)注入到几个类中。

我不想使用这些可选的依赖项来混淆所有类的构造函数,但我找不到在Unity中处理这个问题的好方法。根据{{​​3}},您可以通过向属性添加属性来实现此目的:

private ILogger logger; 

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

我发现这非常难看。在MSDN documentation中,可以执行以下操作来设置给定类型的所有属性:

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

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

修改

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

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

8 个答案:

答案 0 :(得分:18)

我也不喜欢这些属性

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

首先注册类型

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>()));

答案 1 :(得分:16)

我也不是使用属性的忠实粉丝,但我也不喜欢.Configure<InjectedMembers>()方法,因为您绑定了特定的属性名称和特定值。我发现的方式为您提供了最大的灵活性,就是创建自己的构建器策略。

我创建了这个简单的类,它迭代正在构建的对象的属性,并且如果已经使用unity容器注册了该属性的类型,则设置其属性值。

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

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

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

这样就可以在Unity容器中注册:

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

希望这有帮助,
马修

答案 2 :(得分:7)

您发布的原始示例看起来非常麻烦,但您可以使用此类自动实现的属性来帮助清理该代码:

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

答案 3 :(得分:5)

使用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; }

答案 4 :(得分:4)

你可以试试这个:

MyClass中的这段代码

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

然后通过以下方式调用它:

unitycontainer.BuildUp<MyClass>(new MyClass());
然后,

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

答案 5 :(得分:3)

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

答案 6 :(得分:1)

查看基于约定的配置的UnityConfiguration项目。虽然我在使用ResolveAll<IType>()查找多个实现时遇到问题,但它运行良好。请参阅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>();
                    }));

答案 7 :(得分:0)

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

例如,如果你有一个班级&#34; MyClass&#34;并且你依赖于两个接口&#34; IExample1&#34;和&#34; IExample2&#34;并且您不想在构造函数中为它们定义参数,然后按照以下步骤进行操作

步骤1。在Unity容器中注册两个接口

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步。通过这种方式,您可以解析任意数量的依赖项,而无需在构造函数中定义它们。