Using Unity's dependency injection in (data) annotations

那年仲夏 提交于 2019-12-03 08:51:35
Marco Klein

I have solved it myself!

First of all, you need the following Unity Extension and Strategy:

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

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

}

You need this, because it is responsible for injecting the properties on "BuildUp". Next to this, you need to register your extension

container.AddNewExtension<RecursiveBuildUpContainerExtension>();

Furthermore, you need to override the default DataAnnotationsModelMetadataProvider, because the default ModelMetaDataProvider does not use Unity to inject properties to annotations. To do this, implement this class:

public class DynamicModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
    private IUnityContainer _context;

    protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
    {           
        foreach (Attribute attribute in attributes)
            _context.BuildUp(attribute);

        return base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
    }

    public DynamicModelMetadataProvider(IUnityContainer context)
        : base()
    {
        this._context = context;
    }
}

After that, edit your bootstrapper and set the new ModelMetadataProvider, to make it clear to the MVC Framework that it has to use it:

ModelMetadataProviders.Current = new DynamicModelMetadataProvider(container);

Where container is your set up IUnityContainer. You should now have instances in your Annotations Instance when having set the [DependencyAttribute] and your Methods marked with the [InjectionMethod] should get called.

[Dependency]
public ICultureContext Context { get; set; }

Hope you could use this if you had a similar problem ;)

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