开发者

Auto inject property for register types without DependencyAttribute

Register<IA, A>();

class B { public IA A {get;set;}} 

//container inject this property because IA was registered

In autofac you can do

builder.RegisterType<A>().InjectProperties();

for this.

Is there any e开发者_开发知识库xtension for unity to do this? Or may be extension which I can use as sample for implement this feature by myself?


This is not a simple task. To resolve it you should change Unity's default behaviour of the property selector policy. You can change it. This is what I can propose

namespace Microsoft.Practices.Unity.ObjectBuilder
{
    public class ResolveBecouseWeCanPropertySelectorPolicy : PropertySelectorBase<DependencyResolutionAttribute>
    {
        private readonly IUnityContainer container;

        public ResolveBecouseWeCanPropertySelectorPolicy(IUnityContainer container) {
            this.container = container;
        }

        public override IEnumerable<SelectedProperty> SelectProperties(IBuilderContext context, IPolicyList resolverPolicyDestination) {
            Type t = context.BuildKey.Type;
            foreach (
                PropertyInfo prop in
                    t.GetProperties(BindingFlags.Public | BindingFlags.SetProperty | BindingFlags.Instance)) {
                if (prop.GetIndexParameters().Length == 0 && 
                    prop.CanWrite
                    && prop.IsDefined(typeof (DependencyResolutionAttribute), false)
                    ) //Default behaviour
                {

                    yield return CreateSelectedProperty(context, resolverPolicyDestination, prop);

                }
                //Alternate behaviour
                else if (prop.GetIndexParameters().Length == 0 && 
                         prop.CanWrite && 
                    container.IsRegistered(prop.PropertyType)//don't mind about Dependency attribute if we can resolve it - we would
                    ) {
                             {
                        yield return CreateSelectedPropertyForResolveBecouseWeCan(context, resolverPolicyDestination, prop);
                             }
                }
            }
        }

        private SelectedProperty CreateSelectedProperty(IBuilderContext context, IPolicyList resolverPolicyDestination, PropertyInfo property) {
            return DoCreateSelectedProperty(property, resolverPolicyDestination, CreateResolver(property), context);
        }

        private static SelectedProperty CreateSelectedPropertyForResolveBecouseWeCan(IBuilderContext context, IPolicyList resolverPolicyDestination, PropertyInfo property) {
            IDependencyResolverPolicy dependencyResolverPolicy = new DependencyAttribute().CreateResolver(property.PropertyType);
            return DoCreateSelectedProperty(property, resolverPolicyDestination, dependencyResolverPolicy, context);
        }

        private static SelectedProperty DoCreateSelectedProperty(PropertyInfo property, IPolicyList resolverPolicyDestination, IDependencyResolverPolicy dependencyResolverPolicy, IBuilderContext context) {
            string key = Guid.NewGuid().ToString();
            var result = new SelectedProperty(property, key);
            resolverPolicyDestination.Set( dependencyResolverPolicy, key);
            DependencyResolverTrackerPolicy.TrackKey(context.PersistentPolicies,
                                                     context.BuildKey,
                                                     key);
            return result;
        }


        protected override IDependencyResolverPolicy CreateResolver(PropertyInfo property)
        {
            var attributes =
                property.GetCustomAttributes(typeof (DependencyResolutionAttribute), false)
                .OfType<DependencyResolutionAttribute>()
                .ToList();

            Debug.Assert(attributes.Count == 1);

            return attributes[0].CreateResolver(property.PropertyType);
        }
    }
}

This is modified DefaultUnityPropertySelectorPolicy wich you can find in source code of unity.

After that you need to redefine default behaviour by using UnityContainerExtension mechanism.

  public class ResolveBecouseWeCanUnityContainerExtension : UnityContainerExtension {
        protected override void Initialize() {
            Context.Policies.SetDefault<IPropertySelectorPolicy>(
                   new ResolveBecouseWeCanPropertySelectorPolicy(Container));
        }
    }

So now let's imagine that you have following classes

public interface IConcreteService {
        int Val { get; set; }
    }
    public class ConcreteService : IConcreteService {

        public int Val { get; set; }

        public ConcreteService() {
        }
    }

    public class B {
        //no attribute
        public IConcreteService ConcreteService { get; set; }
        public int SomeVal { get; set; }
    }

so now this test should pass

[TestMethod]
        public void TestMethod1() {
            var container = new UnityContainer();
            container.AddExtension(new ResolveBecouseWeCanUnityContainerExtension());


            container.RegisterType<IConcreteService, ConcreteService>();


            var b = new B();
            container.BuildUp(b);
            Assert.IsNotNull(b.ConcreteService);
        }

But you should understand that this one would not

[TestClass]
public class UnitTest1 {
    [TestMethod]
    public void TestMethod1() {
        var container = new UnityContainer();

        container.RegisterType<IConcreteService, ConcreteService>();


        var b0 = new B();
        container.BuildUp(b0);
        Assert.IsNull(b0.ConcreteService);

        container.AddExtension(new ResolveBecouseWeCanUnityContainerExtension());


        var b = new B();
        container.BuildUp(b);
        Assert.IsNotNull(b.ConcreteService); //dies becouse plan cashed
    }
}

Update

I've played a little bit more and found out how to make this test work

[TestClass]
public class UnitTest1 {
    [TestMethod]
    public void TestMethod1() {
        var container = new UnityContainer();

        container.RegisterType<IConcreteService, ConcreteService>();

        var b0 = new B();
        container.BuildUp(b0);
        Assert.IsNull(b0.ConcreteService);

        var b = new B();
        container.BuildUpAndResolveAllWeCan(b);
        Assert.IsNotNull(b.ConcreteService);
        //check if we have no broken something and that it will work second time
        var b1 = new B();
        container.BuildUp(b1);
        Assert.IsNull(b1.ConcreteService);

        var b2 = new B();
        container.BuildUpAndResolveAllWeCan(b2);
        Assert.IsNotNull(b2.ConcreteService);
    }
}

this is can be done in following way

public static class UnityExtensions {

        public static T BuildUpAndResolveAllWeCan<T>(this IUnityContainer container, T existing) {
            return BuildUpAndResolveAllWeCan(container, existing, null) ;
        }

        public static T BuildUpAndResolveAllWeCan<T>(this IUnityContainer container, T existing, string name) {
            container.AddExtension(new ResolveBecouseWeCanUnityContainerExtension());
            //we are adding __BuildUpResolveAllWeCan__ to create new IBuildPlanPolicy for type T 
            //and IPropertySelectorPolicy is ResolveBecouseWeCanPropertySelectorPolicy
            //this will be cached so it will not hurt the performance
            var buildedUp = container.BuildUp(existing, (name ?? string.Empty) + "__BuildUpResolveAllWeCan__");
            container.AddExtension(new CleanUnityContainerExtension());
            return buildedUp;
        }
    }

and adding clean up extension

 public class CleanUnityContainerExtension : UnityContainerExtension {
        protected override void Initialize() {
            Context.Policies.SetDefault<IPropertySelectorPolicy>(
                    new DefaultUnityPropertySelectorPolicy());
        }
    }

You can download source file from here http://92.248.232.12/UnitTest1.zip

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜