17

I am trying to understand the delegate factory pattern with Autofac. I know how to implement factory using IIndex<> with Keyed() registration, which is explained nicely in here: Configuring an Autofac delegate factory that's defined on an abstract class

I would like to know if I can create a factory using Func<>, and how would I do the registrations for the following sample:

public enum Service
{
   Foo,
   Bar
}

public interface FooService : IService 
{
   ServiceMethod();
}

public interface BarService : IService 
{
   ServiceMethod();
}

public class FooBarClient
{
   private readonly IService service;

   public FooBarClient(Func<Service, IService> service)
   {
      this.service = service(Service.Foo);
   }

   public void Process()
   {
      service.ServiceMethod(); // call the foo service.
   }
}
Community
  • 1
  • 1
Narayan Akhade
  • 2,694
  • 3
  • 21
  • 30
  • Why don't you just use `IIndex<>` with `Keyed()`? Autofac cannot create this `Func` for you. You need to register it in your container with using `Keyed()` or `Named()` something like: `builder.Register>(c => s => c.ResolveKeyed(s));` Delegate factories can only create one type with parameters and not choose a type based on parameter because this is what is `IIndex<>` for. – nemesv Mar 15 '13 at 07:26
  • 3
    For IIndex<> I will need to reference the Autofac library which I am trying to avoid. I want my DI code to be in Composite root (separate library) only if possible. – Narayan Akhade Mar 15 '13 at 08:10

1 Answers1

22

Autofac cannot construct this Func<Service, IService> for you which lets you return different types based on a parameter. This is what IIndex<> is for.

However if you don't want/cannot use IIndex<> you can create this factory function with the help of the Keyed or Named and register your factory in the container:

var builder = new ContainerBuilder();
builder.RegisterType<FooBarClient>().AsSelf();
builder.RegisterType<FooService>().Keyed<IService>(Service.Foo);
builder.RegisterType<BarService>().Keyed<IService>(Service.Bar);

builder.Register<Func<Service, IService>>(c => 
{
    var context = c.Resolve<IComponentContext>();
    return s => context.ResolveKeyed<IService>(s);
});
Community
  • 1
  • 1
nemesv
  • 138,284
  • 16
  • 416
  • 359
  • thanks nemesv! its working as expected!!! jus one question, will there be any difference in performance - IIndex vs Func? – Narayan Akhade Mar 15 '13 at 09:47
  • I don't know how IIndex is implemented and what kind of chaching or performance optimization it has. You can check the implantation or you need make performance tests tailored for your scenario to compare the two. – nemesv Mar 15 '13 at 09:52
  • 3
    I just tried that with the reason version, but it gives me an `ObjectDisposedException` ... I "resolved" this by calling `new Foo` inside the delegate (which is a biiiig nogo!) –  Jun 02 '15 at 13:34
  • 1
    @AndreasNiedermair the factory registration in the answer has an issue that went previously unchecked in autofac. Since we cannot "capture" the initial context you need to register an additional resolve of the context like so: `builder.Register>(c => { var context = c.Resolve(); return (s) => {return context.ResolveKeyed(s); };});` – felickz Jul 09 '15 at 19:37