How do I configure a decorator with Castle Windsor

2019-03-04 16:52发布

问题:

I have a decorator and actual implementation that looks like this:

public interface IAmUsedTwice
{
    void DoSomething();
}

public class ForReal: IAmUsedTwice
{
    public SomethingElse Need { get; set; }

    public ForReal(SomethingElse iNeed)
    {
        Need = iNeed;
    }

    public void DoSomething()
    {
        Console.WriteLine("Realing doing something here");
    }
}

public class SomethingElse {}

public class DecoratingIsFun: IAmUsedTwice
{
    private IAmUsedTwice Actual { get; set; }

    public DecoratingIsFun(IAmUsedTwice actual)
    {
        Actual = actual;
    }

    public void DoSomething()
    {
        Console.WriteLine("This is a decorator!");
        Actual.DoSomething();
    }
}

and the configuration was set up before I started this using xml for the actual implementation and looks something like this:

<component id="forReal"
           service="SomeNamespace.IAmUsedTwice, SomeNamespace"
           type="SomeNamespace.ForReal, SomeNamespace">
  <parameters>
    <iNeed>${iNeed}</iNeed>        
  </parameters>
</component>

and you can assume that the iNeed component is set up correctly already.

Now, the system is already configured to use the ForReal class, but what I want to do is swap out the ForReal class and use the DecoratingIsFun class now.

I created an installer to register the DecoratingIsFun class like so:

public class DecoratorInstaller: IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(
            Component.For<IAmUsedTwice>()
                .ImplementedBy<DecoratingIsFun>()
        );
    }
}

However, I still need to tell it two things.

  1. When it resolves IAmUsedTwice I want it to resolve an instance of DecoratingIsFun from now on instead of the other class
  2. When resolving DecoratingIsFun I need it to resolve ForReal as a constructor argument for the instance it's creating.

The goal will be that I can then call windsorContainer.Resolve() and get a DecoratingIsFun instance.

How can I tell the installer to do that?

回答1:

In order to allow DecoratingIsFun to decorate ForReal, you need to ensure that DecoratingIsFun is registered before ForReal - then Windsor will correctly resolve the decorator and satisfy its dependency with the next registration of something that implements IAmUsedTwice.

But since you're using XML to register the first service, I don't know how to achieve that, since the XML is sucked up when you instantiate WindsorContainer.

But why are you using XML in the first place? Is it because you don't think that you can use XML to configure components unless you're also using it to register them?

If that is the case, you should reduce the XML to something like this:

<component id="forReal">
    <parameters>
        <iNeed>${iNeed}</iNeed>        
    </parameters>
</component>

and move the registration to your code, allowing you to control the order of registration. Then, make sure that ForReal is registered with .Named("forReal"), allowing the configuration to be matched when the instance is resolved.