简体   繁体   中英

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.

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"];
        // ...
    }
}

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.

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

https://github.com/czmirek/AddFactoryExtension

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM