I am using SimpleInjector as my IoC Container. I have created a Publish/Subscribe framework for which I now need to be able to refine by enabling subscribers to indicate their priority for execution. For example I could create a subscriber for preloading all of the data required for the remainder of a transaction.
One method I can think of is to create a [SubscriberPriority]
attribute to indicate the requirement during the registration process RegisterManyForOpenGeneric
but I haven't delved any deeper as yet.
Is there a way to manage the order that GetAllInstances
returns registrations?
Update:
Another option I've just thought of would be to use CoC (Convention over Configuration) to order the instances as they are returned by GetAllInstances
.
Since you're indicating that you are using GetAllInstances
, I suspect that you use an RegisterManyForOpenGeneric
overload that takes a callback delegate, as follows:
container.RegisterManyForOpenGeneric(
typeof(ISubstriber<>),
(type, impls) => container.RegisterAll(type, impls),
typeof(ISubstriber<>).Assembly);
In that case the order of which the list of implementations is supplied to the delegate depends on the order in which they are returned from the .NET metadata. RegisterManyForOpenGeneric
simply calls GetExportedTypes()
on the supplied assembly. The order in which these types are returned is highly unreliable and might in a future version of C# even change by recompiling the code and in a future version of the CLR just by restarting the application.
So if the implementations must be placed in the list in a certain order, all you have to to is to order the list of implementations:
(serviceType, impls) => container.RegisterAll(serviceType,
impls.OrderBy(type => [some condition]));
However, in general I would say that the order of things should not matter. If they do matter, take a good look at your design. There might be some issues here. For instance, the fact that you want to mark some classes with the [SubscriberPriority]
attribute, indicates that you might be missing an abstraction. Perhaps you should give them their own interface and register them seperately.
Another thing that I would always advice to do is to hide the list of registrations from the application by placing them behind an composite:
public class CompositeSubscriber<T> : ISubscriber<T>
{
private IEnumerable<ISubscriber<T>> subscribers;
public CompositeSubscriber(
IEnumerable<ISubstriber<T>> subscribers)
{
this.subscribers = subscribers;
}
public void Handle(T value)
{
foreach (var subscriber in this.subscribers)
{
subscriber.Handle(value);
}
}
}
This composite can be registered as follows:
container.RegisterSingleOpenGeneric(typeof(ISubscriber<>),
typeof(CompositeSubscriber<>);
This case the application can simply depend on ISubscriber<T>
instead of IEnumerable<ISubscriber<T>>
. Instead of controlling the order using the callback of the RegisterManyForOpenGeneric
method, now you can use the composite to control the order as well.