Reputation: 5157
I have an application which does data processing. There is
class Pipeline {
IEnumerable<IFilter> Filters {get; set;}
I register filters implementations as
builder.RegisterType<DiversityFilter>().As<IFilter>();
builder.RegisterType<OverflowFilter>().As<IFilter>();
...
So far so good. Now, for experimentation and fine-tuning I want to be able to override any filter implementation in config file with a program(script) which would read data from stdin, process it and send data to stdout. I've implemented a module with "fileName", "args" and "insteadOf" custom properties, described module in xml and got it called.
In the module I register my "ExecutableFilter" but how do I make it run "instead of" desired service? If I try do it like this:
builder.RegisterType<ExecutableFilter>().As<DiversityFilter>()
then I get an exception " The type 'ExecutableFilter' is not assignable to service 'DiversityFilter'.". Ok, this is logical. But what are my options then?
Upvotes: 8
Views: 12350
Reputation: 31757
Once you've overridden the registration for IFilter "After" with your wire-tap, you won't be able to resolve it from the container, as the new registration will be activated instead, hence the circular lookup.
Instead, create and register a module that hooks into the filter's creation, and replaces the instance with the 'wire tapped' one:
class WiretapModule : Module
{
override void AttachToComponentRegistration(
IComponentRegistration registration,
IComponentRegistry registry)
{
if (registration.Services.OfType<KeyedService>().Any(
s => s.ServiceKey == After && s.ServiceType == typeof(IFilter)))
{
registration.Activating += (_, e) =>
{
e.Instance = new WireTap(
(IFilter)e.Instance,
new ExecuteProvider(fileName, args));
};
}
}
}
(Cross-posted to the Autofac group: https://groups.google.com/forum/#!topic/autofac/yLbTeuCObrU)
Upvotes: 11
Reputation: 1388
It's been a while since this question was asked, but I think there is now a simpler solution.
You can register a decorator with a condition. It doesn't matter if the "decorator" is really a decorator or a completely new implementation
builder.RegisterDecorator<ExecutableFilter, IFilter>(c => c.ImplementationType == typeof(DiversityFilter));
This way the concrete type 'DiversityFilter' will be replaced with 'ExecutableFilter'
Upvotes: 0
Reputation: 33910
What you describe is part container work, part business logic. The challenge is to keep separation of concerns here. IMO, the container should do what it is supposed to do, that is building and serving up instances or collections thereof. It should not do the "instead of" in this case. I would rather "enrich" the services with enough information so that the pipeline make the decision.
The "enrichment" can be accomplished by making the ExecutableFilter
implement a more distinct interface.
interface IInsteadOfFilter : IFilter { }
...
builder.RegisterType<ExecutableFilter>().As<IFilter>();
...
class Pipeline
{
IEnumerable<IFilter> Filters {get;set;}
public void DoTheFiltering()
{
var filters = Filters.OfType<IInsteadOfFilter>();
if (!insteadof.Any())
filters = Filters;
foreach(var filter in filters)
{
...
}
}
You could also solve this using the metadata infrastructure, which gives us an even more expressive way of differentiating services.
Upvotes: 2