Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replace registration in Autofac

Tags:

autofac

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?

like image 269
Vadym Chekan Avatar asked Feb 15 '11 01:02

Vadym Chekan


2 Answers

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 += (s, 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)

like image 108
Nicholas Blumhardt Avatar answered Sep 20 '22 10:09

Nicholas Blumhardt


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.

like image 40
Peter Lillevold Avatar answered Sep 21 '22 10:09

Peter Lillevold