2

Recently, Castle added support for interface factories with implementations provided by the kernel. I am wondering if there is a way to do this in autofac also. I have read about the delegate factories, but I think I might be missing something, and am unable to get it to work. Here is what I am thinking:

class Message { }

interface IHandle<T> {
    void Handle(T message);
}

class Handle<Message> : IHandle<Message> {
    ...
}

class Bus {
    .ctor (? lookup) {
        _lookup = lookup;
    }

    void Send<T>(T message) {
        _lookup.GetHandler<T>().Handle(message);
    }
}

var builder = new ContainerBuilder();
builder.RegisterType<Handle<Message>>().As<IHandle<Message>>();
builder.RegisterType<Bus>();

var container = builder.Build();
container.Resolve<Bus>().Send<Message>(new Message());

What I'm trying to do is keep the container out of the bus (as I agree that service locator is an anti-pattern) or any other implementation. This problem is easy if I just feed the container to the bus, or create some class factory that wraps the container. Just trying to make sure there isn't a way to do this already.

Btw, the castle way iirc allows me to register something like this:

interface IHandlerFactory {
    IHandle<T> GetHandler<T>();
}

container.Register<IHandlerFactory>().AsFactory();

Thanks, Nick

neouser99
  • 1,807
  • 1
  • 10
  • 23
  • The Castle approach looks very clever - Autofac doesn't support this. I'm of the opinion that @kcalvanti's approach below is best for this scenario, however you can also use Autofac's `IIndex` type to similar effect, with a little more configuration. – Nicholas Blumhardt Dec 24 '11 at 16:30
  • @NicholasBlumhardt Yep, I'm going to accept his answer for now. If it changes in the future, I can definitely revisit. – neouser99 Dec 26 '11 at 17:26

2 Answers2

4

Seems like the answer is 'No'.

I've tried to use AggregateService which is supposed to do what you want, but it crashes during factory resolution with message 'The type "Castle.Proxies.IComponentFactoryProxy" from assembly "DynamicProxyGenAssembly2, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" has tried to implement an unaccessible interface.' (The original message is in Russian, I've translated it).

I can't figure out another approach that would work without a manual (yet quite simple) implementation. Maybe you should write to Nicholas Blumhardt.

In fact, I'd better do it this way:

public class Bus {
  readonly ILifetimeScope _OwnScope;

  // Autofac always provides ILifetimeScope that owns a component as a service to the component
  // so it can be used as a dependency
  public Bus(ILifetimeScope ownScope) {
    _OwnScope = ownScope;
  }

  void Send<T>(T message) {
    _OwnScope.Resolve<IHandler<T>>.Handle(message);
  }
}

You will probably argue that coupling with ILifetimeScope is a bad idea. Well, it's quite a simple interface which might be wrapped into your own implementation. Or you may factor the code out into a simple factory class. Well, it seems like you know how to do this without my suggestions.

Pavel Gatilov
  • 7,579
  • 1
  • 27
  • 42
  • I have seen info about the aggregateservice before, but glanced over it. I think that might definitely be the way to go. I would agree, ILifetimeScope is a bad idea as that leaks autofac into my projects where it shouldn't be. A IHandlerFactory with autofacimpl would be the way I would go without getting too leaky. – neouser99 Dec 22 '11 at 15:26
  • @neouser99 Yeah, I just wish it worked. If you manage to make it work, please post some hints on the setup. Maybe I used some wrong Castle assemblies or whatever. – Pavel Gatilov Dec 22 '11 at 16:55
4

You could isolate that coupling by creating a concrete IHandlerFactory, say AutofacHandlerFactory, which would receive the ILifetimeScope. That coupling seems inevitable since the container is the only one who can resolve the proper IHandler<T>.

Coupling with ILifetimeScope might be a bad idea, but then, the coupling is isolated inside the concrete IHandlerFactory, and the Bus just uses it through an interface. Let's say you change the container and starts using Ninject, you could just implement a NinjectHandlerFactory to do the job.

rcaval
  • 762
  • 5
  • 12
  • If the functionality doesn't exist to duplicate the Castle feature, this would definitely be the way to go (and is in fact how I have it now). – neouser99 Dec 22 '11 at 15:27