Order when calling ResolveAll in Castle Windsor
Asked Answered
H

2

5

Assume that I have multiple objects registered in the container, all implementing the same interface:

container.Register(
    Component.For<ITask>().ImplementedBy<Task1>(),
    Component.For<ITask>().ImplementedBy<Task2>(),
    Component.For<ITask>().ImplementedBy<Task3>(),
    Component.For<ITask>().ImplementedBy<Task4>(),
);

And I wish to resolve all the implementations of ITask:

var tasks = container.ResolveAll<ITask>();

Is there a way to control the order of the resolved instances?

Note: Obviously, I can implement an Order or Priority property on ITask, and just sort the list of tasks, but I am looking for a lower-level solution.

Hooded answered 27/10, 2014 at 8:30 Comment(4)
+1. This problem really bugs me since the handler filter needs to know something about implementations to sort them. I hope you will get some better ways to do it out of your question :)Teaching
@Teaching I'm totally with you, mate. But, I have to admit that this is not Windsor castle's problem. To the best of my knowledge -sorting entities/component is part of a system's business logic and which is obviously not what Windsor Castle was created for. It might have gone a bit far with a few "cool" featuresRanie
@Saguiltay if you are concerned about adding too many dependencies to windsor castle's initialization -ironic DI and IoC are supposed to break dependencies- then you should create a wrapper component that does all the logic of sorting, filtering, etc. Make it implement an interface that exposes a method to return these tasks and finally, register this wrapper as you'd normally would with Windsor CastleRanie
@Ranie I totally agree. In fact I read mookid8000 blog post about its sorting system which was exactly how I envisionned doing it; don't use absolute order but try and coax some adequate order from requirements (a before b, c after a, etc). My frustration certainly comes from the fact that the doc was not very explicit on these featuresTeaching
R
6

I believe that what you are looking for is a Handler Filter which based on the scarce documentation and the breaking changes in Windsor Castle 3; it provides filtering and sorting of components. Here's an extract from their Wiki page

The interface is similar to IHandlerSelector but allow you to filter/sort handlers requested by container.ResolveAll() method

So basically you will need to implement the IHandlerFilter interface and then add this implementation to the kernel while initializing the Windsor container. From the source code, this interface looks something similar to this...

public interface IHandlerFilter
{
    bool HasOpinionAbout(Type service);
    IHandler[] SelectHandlers(Type service, IHandler[] handlers);
}

Adding the Handler Filter to the kernel would be something like below...

 var container = new WindsorContainer()
        .Install(Configuration.FromXmlFile("components.config"));
        container.Kernel.AddHandlersFilter(new YourHandlerFilterImplementation());

and then when resolving all components of a services with ResolveAll the order will be sorted (and filtered) based on your own implementation

Ranie answered 27/10, 2014 at 8:58 Comment(0)
A
4

I second Leo's advice that what you're after is a handlers filter.

On my blog I have this example on how a simple handlers filter can be made, which happens to be one that results in ordered task instances when they're resolved through ResolveAll<ITask> (or when an IEnumerable<ITask> gets injected):

class TaskHandlersFilter : IHandlersFilter
{
    readonly Dictionary<Type, int> sortOrder = 
        new Dictionary<Type, int>
        {
            {typeof (PrepareSomething), 1},
            {typeof (CarryItOut), 2},
            {typeof (FinishTheJob), 3},
        };

    public bool HasOpinionAbout(Type service)
    {
        return service == typeof(ITask);
    }

    public IHandler[] SelectHandlers(Type service, IHandler[] handlers)
    {
        // come up with some way of ordering implementations here
        // (cool solution coming up in the next post... ;))
        return handlers
            .OrderBy(h => sortOrder[h.ComponentModel.Implementation])
            .ToArray();
    }
}

I also have a more elaborate example that plays around with the API used to specify the ordering - in this case, I'm allowing for the types to specify their position relative to another type, e.g. like

[ExecutesBefore(typeof(ExecutePayment))]
public class ValidateCreditCards : ITask { ... }

which might/might not be useful in your concrete situation :)

Anuran answered 27/10, 2014 at 9:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.