简体   繁体   中英

How to specify a service override further down the dependency graph in Castle Windsor?

Here is a simplification of my scenario.

I have some interfaces IFoo , IBar , and IBaz with their implementations Foo , Bar and Baz .

Foo depends on IBar and Bar depends on IBaz , so the dependency graph looks like this:

   Foo 
    |
   Bar
    |
   Baz

My Windsor config looks like this, and everything works as expected:

container.Register(Component.For<IFoo>().ImplementedBy<Foo>());
container.Register(Component.For<IBar>().ImplementedBy<Bar>());
container.Register(Component.For<IBaz>().ImplementedBy<Baz>());

Now I need to introduce a new SpecialBaz , and I want to use it instead of the old Baz when my dependency graph begins with a Foo . The rest of the app will continue to the the old Baz :

      Foo           SomethingElse
       |                  |
      Bar                Bar
       |                  |
   SpecialBaz            Baz

I have tried naming the SpecialBaz with the ServiceOverride api:

container.Register(Component.For<IBaz>().ImplementedBy<Baz>().IsDefault());
container.Register(Component.For<IBaz>().Named("special baz").ImplementedBy<SpecialBaz>());

container.Register(Component.For<IFoo>()
                            .ImplementedBy<Foo>()
                            .DependsOn(ServiceOverride.ForKey<IBaz>().Eq("special baz")));

And specifying the dependency:

container.Register(Component.For<IBaz>().ImplementedBy<Baz>().IsDefault());
container.Register(Component.For<IBaz>().ImplementedBy<SpecialBaz>());

container.Register(Component.For<IFoo>()
                            .ImplementedBy<Foo>()
                            .DependsOn(Component.For<IBaz>().ImplementedBy<SpecialBaz>()));

But neither approach worked.

Is this possible in Castle Windsor, and how do I wire it up?

The short answer is yes - Castle Windsor can. Every DI container can. I will offer you a rule to remember by which you can resolve most of your dependency problems.

The long answer.

The rule : Every point of indirection requires a layer of abstraction . Keep this rule in mind when you try to build dependency resolutions in apps. And there are two kinds of indirections as far as I know - static and dynamic.

You need to change the graph at one point and only once ( indirections ) but somehow you need to traverse the dependecies graph at runtime to see if your Baz is requested for Foo or not. So you need a factory of some kind( the abstraction thingie ).

You have two options depending on the answer of one question:

Is my change dependent on static or dynamic conditions?

For example - If you depend on incoming user data or environment data - you are in the dynamic case in which you will need yo use Dynamic Parameters or something else capable.

In your case your dependency is static.

and I want to use it instead of the old Baz when my dependency graph begins with a Foo.

The condition for special Baz never changes once the beginning of the graph is Foo.

You can achieve this with Factory method or Typed Factory Componend Selector. I provide you an example with Factory Method :

using System;
using Castle.MicroKernel.Registration;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            var container = new Castle.Windsor.WindsorContainer();
            container.Register(Component.For<IFoo>().ImplementedBy<Foo>());
            container.Register(Component.For<ISomethingElse>().ImplementedBy<SomethingElse>());

            container.Register(Component.For<IBar>().ImplementedBy<Bar>().LifeStyle.Transient);

            container.Register
            (
                Component
                .For<IBaz>()
                .UsingFactoryMethod
                (
                    (k, c) =>
                    {
                        IBaz resolved = null;
                        var requestingType = c.Handler.ComponentModel.Implementation;
                        if (requestingType == typeof(Foo))
                        {
                            resolved = new Baz();
                        }
                        else
                        {
                            resolved = new BazSpecial();
                        }

                        return resolved;
                    }
                ).LifeStyle.Transient
            );

            var f = container.Resolve<IFoo>();
            var se = container.Resolve<ISomethingElse>();

            Console.ReadLine();
        }
    }
    internal interface IBar
    {
    }

    internal interface IBaz
    {
    }

    internal interface IFoo
    {
    }

    interface ISomethingElse
    {

    }


    internal class Bar : IBar
    {
        private readonly IBaz baz;

        public Bar(IBaz baz)
        {
            this.baz = baz;
        }

    }

    internal class Baz : IBaz
    {
    }

    internal class BazSpecial : IBaz
    {
    }

    internal class Foo : IFoo
    {
        private readonly IBar bar;

        public Foo(IBar bar)
        {
            this.bar = bar;
        }
    }

    internal class SomethingElse : ISomethingElse
    {
        private readonly IBar bar;

        public SomethingElse(IBar bar)
        {


         this.bar = bar;
        }
    }
}}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM