1

Consider the following example:

public interface ITask
{
    void Execute();
}

public class LoggingTaskRunner : ITask
{
    private readonly ITask _taskToDecorate;
    private readonly MessageBuffer _messageBuffer;

    public LoggingTaskRunner(ITask taskToDecorate, MessageBuffer messageBuffer)
    {
        _taskToDecorate = taskToDecorate;
        _messageBuffer = messageBuffer;
    }

    public void Execute()
    {
        _taskToDecorate.Execute();
        Log(_messageBuffer);
    }

    private void Log(MessageBuffer messageBuffer)
    {}
}

public class TaskRunner : ITask
{
    public TaskRunner(MessageBuffer messageBuffer)
    {

    }

    public void Execute()
    {
    }
}

public class MessageBuffer
{

}


public class Configuration
{
    public void Configure()
    {
        IWindsorContainer container = null;

        container.Register(
            Component.For<MessageBuffer>()
                .LifeStyle.Transient);

        container.Register(
            Component.For<ITask>()
                .ImplementedBy<LoggingTaskRunner>()
                .ServiceOverrides(ServiceOverride.ForKey("taskToDecorate").Eq("task.to.decorate")));

        container.Register(
            Component.For<ITask>()
            .ImplementedBy<TaskRunner>()
            .Named("task.to.decorate"));

    }

}

How can I make Windsor instantiate the "shared" transient component so that both "Decorator" and "Decorated" gets the same instance?

Edit: since the design is being critiqued I am posting something closer to what is being done in the app. Maybe someone can suggest a better solution (if sharing the transient resource between a logger and the true task is considered a bad design)

Edit2: Castle3 has added support for this (http://docs.castleproject.org/Windsor.Whats-New-In-Windsor-3.ashx) by introducing the "Bound" lifestyle

Marius
  • 9,208
  • 8
  • 50
  • 73
  • Based on your edit, you don't need the MessageBuffer in TaskRunner at all, since it's not used. I suspect we are still looking at something that is too far removed from the actual problem at hand? I know it can be difficult to distill a problem so that it can easily be communicated without warping it beyond recognition, but based on the information at hand, you can solve the problem by removing the MessageBuffer dependency from TaskRunner... – Mark Seemann May 28 '10 at 11:32
  • I just removed the usage for brevity :-) The decorated task will put messages in the messagebuffer and the logging decorated will put the messages from the buffer into a logger (log4net, console out, whatever) – Marius May 28 '10 at 12:40

1 Answers1

1

'Transient' explicitly means 'non-shared', so what you are asking is conceptually the wrong thing to do. The correct solution is to register Shared as a Singleton instead of Transient:

container.Register(Component.For<Shared>());

(Singleton is the default lifetime in Windsor.)

However, I suspect that behind the stated question lies a much more complex problem. I'm guessing that you need Shared to be Transient because you need it with this lifestyle for a lot of other cases, but exactly when it comes to the relationship between Decorator and Decorated you need to share them.

I still think this sounds like a Design Smell, but there are at least two ways you can achieve this result.

The first option involves prematurely resolving Shared and explicitly supply the resolved instance to the configuration of the two IFoo registrations:

container.Register(Component.For<Shared>().LifeStyle.Transient);

var r = container.Resolve<Shared>();

container.Register(Component
    .For<IFoo>()
    .ImplementedBy<Decorator>()
    .DependsOn(new { resource = r }));
container.Register(Component
    .For<IFoo>()
    .ImplementedBy<Decorated>()
    .DependsOn(new { resource = r }));

The second option is to make a specialized, named registration for Shared that is used only by the IFoo registrations:

container.Register(Component.For<Shared>().LifeStyle.Transient);
container.Register(Component.For<Shared>().Named("shared"));
container.Register(Component
    .For<IFoo>()
    .ImplementedBy<Decorator>()
    .ServiceOverrides(new { resource = "shared" }));
container.Register(Component
    .For<IFoo>()
    .ImplementedBy<Decorated>()
    .ServiceOverrides(new { resource = "shared" }));
Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • 1
    I don't want to make "Shared" non-transient because it has state. I also don't want to remove it as a constructor dependency because its an invariant which has to be satisfied, making it into a property instead of a ctor dependency would be retrofitting the class design to fit the workings of an IoC container. – Marius May 28 '10 at 10:46
  • Concern about state is a very valid reason why you would not want to share instances, but `Shared` *is* shared between Decorator and Decorated, so you already violate that principle. Your current implementation may behave 'nicely' and not corrupt state, but other collaborators might not be so nice. In other words, implementations may break if injected with different dependencies. That indicates a violation of the Liskov Substitution Principle. – Mark Seemann May 28 '10 at 11:36
  • You are misenterpreting the usage. It is valid for any class which has a reference to a messagebuffer to utilize its contract and by that I mean adding messages. I'm am not assuming anything about the usage of the messagebuffer, thus I am not breaking Liskov. – Marius May 28 '10 at 12:46
  • @MarkSeemann I think my question has a similar usage for to what Marius is asking , Could you please have a look , http://stackoverflow.com/questions/25064516/dependency-injection-lifestyle-service-shared-instance-between-2-instances-of – eran otzap Aug 03 '14 at 17:05