user6417011
user6417011

Reputation:

factory pattern in autofac

I want to implement factory pattern in Autofac. This is how it was done in Ninject:

Bind<ICarFilter>().To<CarFilter >();
Bind<IBikeFilter>().To<BikeFilter>();
Bind<IFilterFacade>().ToFactory().InSingletonScope();

This is a definitnion of IFilterFacade

public interface IFilterFacade
    {    
        ICarFilter CreateCarFilter();

        IBikeFIlter CreateBikeFilter();         
    }

If I want an instance of CarFilter type, all I need to do is the following:

public class HomeController(IFilterFacade filterFacade)
{
    FilterFacade = filterFacade;
}

public IFilterFacade FilterFacade { get;set; }

public ActionResult Index()
{
  var bikeFilter = FilterFacade.CreateBikeFilter();
}

Any example how to do that in AutoFac. I checked AutoFac documentation, but was not able to find the answer or example.

Upvotes: 7

Views: 7985

Answers (3)

Miroslav
Miroslav

Reputation: 4735

I wrote a NuGet which does exactly this but over IServiceCollection.

https://github.com/czmirek/AddFactoryExtension

https://www.nuget.org/packages/AddFactoryExtension

Upvotes: 1

Cyril Durand
Cyril Durand

Reputation: 16187

There is no equivalent of ToFactory in Autofac but you can easily implement a generic factory equivalent.

public interface IFilterFactory
{
    TFilter Get() where TFilter : IFilter; 
}

public class FilterFactory : IFilterFactory
{

    public FilterFactory(ILifetimeScope scope)
    {
        this._scope = scope; 
    }

    private readonly ILifetimeScope _scope; 

    public TFilter Get<TFilter>()
    {
        return this._scope.Resolve<TFilter>(); 
    }
}

Then register it like this :

builder.RegisterType<FilterFactory>().As<IFilterFactory>(); 
builder.RegisterType<BikeFilter>().As<IBikeFilter>(); 
builder.RegisterType<CarFilter>().As<ICarFilter>(); 

and use it this way

public class HomeController
{ 
    private readonly IFilterFactory _filters;

    public HomeController(IFilterFactory filters)
    {
        this._filters = filters; 
    }

    public ActionResult Index()
    {
        var bikeFilter = this._filters.Get<IBikeFilter>();
        // ...
    }
}

I rename the Create method to Get because the method will look up an instance in the lifetime scope and create one only if needed.

Another possible solution may be to use Named and Keyed Service and the built-in IIndex type.

builder.RegisterType<BikeFilter>().Named<IFilter>("Bike"); 
builder.RegisterType<CarFilter>().Named<IFilter>("Car");

then on your controller

public class HomeController
{ 
    private readonly IIndex<String, IFilter> _filters;

    public HomeController(IIndex<String, IFilter> filters)
    {
        this._filters = filters; 
    }

    public ActionResult Index()
    {
        var bikeFilter = this._filters["Bike"];
        // ...
    }
}

Upvotes: 9

felix-b
felix-b

Reputation: 8498

Autofac has no such feature as dynamically implementing user-defined factory interface. It does allow binding to delegates, but it's not the same.

If you want to receive a kind of factory in constructor, then obtain individual components from that factory in relevant methods, it is definitely possible with Autofac.

Composition root

Here I register individual components as singletons (this was the behavior with Ninject, if I get it correctly), but you can use any lifetime option that fits your requirements.

ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<BikeFilter>().As<IBikeFilter>().SingleInstance();
builder.RegisterType<CarFilter>().As<ICarFilter>().SingleInstance();

Controller

public class HomeController
{ 
    private readonly IComponentContext _components;

    public HomeController(IComponentContext components)
    {
        _components = components; // instead of factory interface
    }

    public ActionResult Index()
    {
        var bikeFilter = _components.Resolve<IBikeFilter>();
        // ...
    }
}

BTW

I think that factory interface is a nice feature of Ninject. Another added value of it is allowing to explore through IntelliSense. In your example, when I type FilterFacade and the dot character, I'm presented with a list of supported filters (CreateCarFilter, CreateBikeFilter, ...). So that even if I'm not fully familiar with the project codebase, I can pick one that fits best. With Autofac, IComponentContext allows me request any component, which doesn't help me find out what relevant components exist, or which components I'm expected to request.

Upvotes: 0

Related Questions