Configuring Unity to resolve a type that takes a decorated dependency that has a parameter that varies with the type into which it is injected

后端 未结 2 1833
清酒与你
清酒与你 2021-02-05 20:15

This is a fairly straight forward decorator pattern scenario, with the complication that the decorated type has a constructor parameter that is dependent on the type into which

相关标签:
2条回答
  • 2021-02-05 20:51

    Have you ever thought about basing your decorators on the Unity Interception functionality? Then it would be really easy to say "intercept the calls to IThing using this Interceptor" just once.

    container.AddNewExtension<Interception>();
    container.RegisterType<IThing>(new Interceptor<InterfaceInterceptor>(), new InterceptionBehavior<DecoratingThingBehavior>());
    

    and then it would be "inject this IThing into this and that Depender"

    container.RegisterType<Depender1>(new InjectionConstructor(new ResolvedParameter<IThing>("myNameForThing")));
    
    0 讨论(0)
  • 2021-02-05 20:52

    The class design itself seems reasonable. Here's a convention-based container configuration that basically does this:

    public class MyConventions : UnityContainerExtension
    {
        protected override void Initialize()
        {
            var dependers = from t in typeof(IThing).Assembly.GetExportedTypes()
                            where t.Name.StartsWith("Depender")
                            select t;
    
            foreach (var t in dependers)
            {
                var number = t.Name.TrimStart("Depender".ToArray());
                var realName = "Real" + number;
                var decoName = "Deco" + number;
                var config = "Config" + number;
                this.Container.RegisterType<IThing, RealThing>(realName, 
                    new InjectionConstructor(config));
                this.Container.RegisterType<IThing, DecoratingThing>(decoName,
                    new InjectionConstructor(
                        new ResolvedParameter<IThing>(realName)));
                this.Container.RegisterType(t,
                    new InjectionConstructor(
                        new ResolvedParameter<IThing>(decoName)));
            }
        }
    }
    

    This configuration will automatically add all classes that match the above predicate, so once you've set it up, you can just add more classes (like Depender4 or Depender5) without revisiting the container configuration at all.

    The above configuration satisfies these unit tests:

    [Fact]
    public void ContainerCorrectlyResolvesDepender1()
    {
        var container = new UnityContainer().AddNewExtension<MyConventions>();
        var actual = container.Resolve<Depender1>();
    
        var deco = Assert.IsAssignableFrom<DecoratingThing>(actual.Thing);
        var thing = Assert.IsAssignableFrom<RealThing>(deco.Thing);
        Assert.Equal("Config1", thing.Configuration);
    }
    
    [Fact]
    public void ContainerCorrectlyResolvesDepender2()
    {
        var container = new UnityContainer().AddNewExtension<MyConventions>();
        var actual = container.Resolve<Depender2>();
    
        var deco = Assert.IsAssignableFrom<DecoratingThing>(actual.Thing);
        var thing = Assert.IsAssignableFrom<RealThing>(deco.Thing);
        Assert.Equal("Config2", thing.Configuration);
    }
    
    [Fact]
    public void ContainerCorrectlyResolvesDepender3()
    {
        var container = new UnityContainer().AddNewExtension<MyConventions>();
        var actual = container.Resolve<Depender3>();
    
        var deco = Assert.IsAssignableFrom<DecoratingThing>(actual.Thing);
        var thing = Assert.IsAssignableFrom<RealThing>(deco.Thing);
        Assert.Equal("Config3", thing.Configuration);
    }
    
    0 讨论(0)
提交回复
热议问题