问题
I'm deserializing a class like this one
class AClass{
[Dependency]
AnotherClass Property{ get; set; }
}
When I then BuildUp() the object I'd like Unity to create a new instance of AnotherClass only if the property is null and otherwise just perform a BuildUp on it. Is there a simple way to achieve this?
Edit: I'm doing mvvm with wpf. The classes are view-models, I serialize them as there are some properties I want to preserve between runs and they also have some dependencies I want unity to inject. So after deserialization the nested model is already there with properties set so I don't want unity to override it with a new instance but I still want it to call InjectionMethods on it and also to resolve it normally the first time the program is run and the nested models are null.
回答1:
I ended up writing a unity extension. I could not find much documentation on how to do this so I'm not really sure about the result. It seems to work but I have this feeling I might have written something horrible.. Anyway here's the code, improvements are welcome:
public class RecursiveBuildUpContainerExtension : UnityContainerExtension {
protected override void Initialize(){
Context.Strategies.Add( new RecursiveBuildUpBuilderStrategy( Context.Container ), UnityBuildStage.PreCreation );
}
}
public class RecursiveBuildUpBuilderStrategy : BuilderStrategy {
readonly IUnityContainer container;
public RecursiveBuildUpBuilderStrategy( IUnityContainer container ) {
this.container = container;
}
public override void PreBuildUp( IBuilderContext context ) {
if( context.Existing == null ) return;
foreach( var prop in context.Existing.GetType( ).GetProperties( ) ) {
if( ContainsType<DependencyAttribute>( prop.GetCustomAttributes( true ) ) ) {
if( prop.GetValue( context.Existing, null ) == null ) {
var value = container.Resolve( prop.PropertyType );
prop.GetSetMethod( ).Invoke( context.Existing, new[] { value } );
}
else {
var value = container.BuildUp( prop.PropertyType, prop.GetValue( context.Existing, null ) );
prop.GetSetMethod( ).Invoke( context.Existing, new[] { value } );
}
}
}
foreach (var method in context.Existing.GetType().GetMethods() ){
if( ContainsType<InjectionMethodAttribute>( method.GetCustomAttributes( true ))){
var argsInfo = method.GetParameters( );
var args = new object[argsInfo.Length];
for( int i = 0; i < argsInfo.Length; i++ ) {
args[i] = container.Resolve( argsInfo[i].ParameterType );
}
method.Invoke( context.Existing, args );
}
}
context.BuildComplete = true;
}
private static bool ContainsType<T>( IEnumerable<object> objects ){
foreach (var o in objects){
if( o is T ) return true;
}
return false;
}
}
回答2:
You can write your own BuildUp
method, like this:
public static void BuildUpButSkipInitializedProperties(
this UnityContainer container, object instance)
{
var registeredTypes = (
from registration in container.Registrations
select registration.RegisteredType)
.ToArray();
var injectableProperties =
from property in instance.GetType().GetProperties()
where property.GetGetMethod() != null
where property.GetSetMethod() != null
where property.GetValue(instance, null) == null
where registeredTypes.Contains(property.PropertyType)
select property;
foreach (var property in injectableProperties)
{
object value = container.Resolve(property.PropertyType);
property.SetValue(instance, value);
}
}
Usage:
var instance = new AClass();
container.BuildUpButSkipInitializedProperties(instance);
I didn't test or even compile this code, but it should pretty much do the trick. Note that since it iterates all Registrations
every time and injects all properties using reflection, it might not be the fasted thing ;-)
回答3:
Is there a simple way? No.
You'd need to edit the generated il, detect already existing objects... It could be done but it's in no way simple.
来源:https://stackoverflow.com/questions/7120476/unitycontainer-buildup-can-i-make-it-inject-new-instances-into-properties-on