问题
I am working on a project where the Unity framework is used as the IoC container. My question relates to injecting an optional dependency (in this case a logger) into several classes using property- or setter injection.
I do not want to clutter the constructors of all my classes with these optional dependencies, but I cannot find a good way to handle this in Unity. The way you would do this is, according to the MSDN documentation, by adding an attribute to the property:
private ILogger logger;
[Dependency]
public ILogger Logger
{
get { return logger; }
set { logger = value; }
}
I find this very ugly. In StructureMap one could do the following to set all properties of a given type:
SetAllProperties(policy => policy.OfType<ILog>());
Does anyone know if it is possible to do something similar in Unity?
Edit:
Kim Major suggests using this approach which can also be achieved through code.
I would be interested in examples of how to do this automatically for all matching properties.
回答1:
I don't like those attributes also
You can do all using the Configure method of the unity container:
First register the type
unityContainer.RegisterType<MyInterface,MyImpl>(
new ContainerControlledLifetimeManager());
If you have multiple constructors you'll have to do this so Unity invokes the parameterless constructor (if none set Unity will go for the fattest one)
unityContainer.Configure<InjectedMembers>()
.ConfigureInjectionFor<MyImpl>(
new InjectionConstructor());
Setting property dependency
unityContainer.Configure<InjectedMembers>()
.ConfigureInjectionFor<MyImpl>(
new InjectionProperty(
"SomePropertyName",
new ResolvedParameter<MyOtherInterface>()));
Configuring method dependency
unityContainer.Configure<InjectedMembers>()
.ConfigureInjectionFor<MyImpl>(
new InjectionMethod(
"SomeMethodName",
new ResolvedParameter<YetAnotherInterface>()));
回答2:
I am also not a big fan of using attributes but I also don't like the .Configure<InjectedMembers>() method because you're bound to a specific property name and specific value. The way I've found that gives you the most flexibility is to create your own builder strategy.
I created this simple class that iterates the properties of an object being built up and sets its property values if the type of that property has been registered with the unity container.
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));
}
}
}
}
}
You register your BuilderStrategy by creating a UnityContainerExtension. Here is an example:
public class TestAppUnityContainerExtension:UnityContainerExtension
{
protected override void Initialize()
{
Context.Strategies.Add(new PropertyInjectionBuilderStrategy(Container), UnityBuildStage.Initialization);
}
}
That gets registered with the Unity container as such:
IUnityContainer container = new UnityContainer();
container.AddNewExtension<TestAppUnityContainerExtension>();
Hope this helps,
Matthew
回答3:
The original example you posted does look very cumbersome, but you can use auto-implemented properties like this to help clean that code up:
[Dependency]
public ILogger Logger { get; set; }
回答4:
With Unity 2.1 this would work as well:
var container = new UnityContainer()
.RegisterType<ILogger, Logger>()
.RegisterType<ISomeInterface, SomeImplementaion>(
new InjectionProperty("Logger", new ResolvedParameter<ILogger>()));
The injected property of SomeImplementaion class is just
public ILogger Logger { get; set; }
回答5:
You could try this:
this code in MyClass
[InjectionMethod]
public void Initialize(
[Dependency] ILogger logger
and then calling it by:
unitycontainer.BuildUp<MyClass>(new MyClass());
unity will then call Initialize method with the dependency from the container and then u can save it in a private variable in MyClass or something...
回答6:
The following walkthrough shows one way of doing it through configuration. You can of course wire it through code as well. http://aardvarkblogs.wordpress.com/unity-container-tutorials/10-setter-injection/
回答7:
Have a look at the UnityConfiguration project for convention-based configuration. It works pretty well, though i have an issue while looking up multiple implementations using ResolveAll<IType>(). See 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:
You can inject your class with unity container.
for example if you have a class "MyClass" and you have dependency on two interface "IExample1" and "IExample2" and you don't want to define parameter for them in constructor , then follow below steps
Step 1. Register both the interfaces in unity container
IUnityContainer container = new UnityContainer();
container.RegisterType<IExample1,Impl1>();
container.RegisterType<IExample2,Impl2>();
//resolve class MyClass
var myClass = container.Resolve<MyClass>();
Step 2. Your class should look like this
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
}
}
Step 3. In this way you can resolve any number of dependencies without defining them in constructor.
来源:https://stackoverflow.com/questions/515028/setter-property-injection-in-unity-without-attributes