UnityContainer.BuildUp() - Can I make it inject new instances into properties only if these are null?

戏子无情 提交于 2019-12-11 07:44:20

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!