Michael McCarthy
Michael McCarthy

Reputation: 1542

Injecting factory using Autofac vs. delegate factories

I'm a new user of Autofac.

I have a factorythat needs to create a different class based on the input to the factory method, but only one of the classes that needs to be created has other dependencies.

I found this answer: Autofac Binding at Runtime

whose solution worked well.

but then I started reading about Autofac's delegate factories (http://docs.autofac.org/en/latest/advanced/delegate-factories.html)... and now I'm confused.

It appears, if you use a delegate factory, then you don't actually have to write a factory class at all?

Here is a snipped from my current factory class:

public class ExternalUserInformationProviderFactory : IExternalUserInformationProviderFactory
{
    private readonly IComponentContext autofacContainer;

    public ExternalUserInformationProviderFactory(IComponentContext autofacContainer)
    {
        this.autofacContainer = autofacContainer;
    }

    public IProvideExternalUserInformation GetExternalUserInformationProvider(string loginProvider)
    {
        switch (loginProvider)
        {
            case "Google":
                return autofacContainer.Resolve<GoogleExternalUserInformationProvider>();
            case "Twitter":
                return autofacContainer.Resolve<TwitterExternalUserInformationProvider>();
        }
        return null;
    }
}

In this example, the TwitterExternalUserInformationProvider takes a dependency in its constructor:

public class TwitterExternalUserInformationProvider : IProvideExternalUserInformation
{
    private readonly ITwitterRepository twitterRepository;

    public TwitterExternalUserInformationProvider(ITwitterRepository twitterRepository)
    {
        this.twitterRepository = twitterRepository;
    }
}

and the GoogleExternalUserInformationProvider takes no constructor args at all.

here is how I have this factory wired up in Startup.cs (I'm using asp.net core):

var containerBuilder = new ContainerBuilder();
containerBuilder.Register<IExternalUserInformationProviderFactory>(c => new ExternalUserInformationProviderFactory(c.Resolve<IComponentContext>()));
containerBuilder.RegisterType<TwitterExternalUserInformationProvider>();
containerBuilder.RegisterType<GoogleExternalUserInformationProvider>();

Autofac is smart enough to resolve the ITwitterRepository dependency for me, which is really cool.

Based on this current implementation, is it possible for me to use a delegate factory and get rid of the ExternalUserInformationProviderFactory altogether?

I'm curious.

Thanks

Upvotes: 0

Views: 1215

Answers (1)

Cyril Durand
Cyril Durand

Reputation: 16187

Delegate factory won't choose between 2 implementations of a service, it will only create a component based on its dependencies.

In your case you need a factory. Instead of depend on IComponentContext, your factory may also depend on IIndex<String, IProvideExternalUserInformation> which may avoid issue with scope & co.

Your ExternalUserInformationProviderFactory could look like this :

public class ExternalUserInformationProviderFactory : IExternalUserInformationProviderFactory
{
    public ExternalUserInformationProviderFactory(IIndex<String, IProvideExternalUserInformation> providers)
    {
        this._providers = providers;
    }

    private readonly IIndex<String, IProvideExternalUserInformation> _providers; 


    public IProvideExternalUserInformation GetExternalUserInformationProvider(String loginProvider)
    {
        IProvideExternalUserInformation provider;
        if (!this._providers.TryGetValue(loginProvider, out provider))
        {
            throw new Exception("boom"); 
        }
        return provider; 
    }
}

and your registration :

builder.RegisterType<TwitterExternalUserInformationProvider>()
       .Named<IProvideExternalUserInformation>("twitter"); 
builder.RegisterType<GoogleExternalUserInformationProvider>()
       .Named<IProvideExternalUserInformation>("google"); 
builder.RegisterType<ExternalUserInformationProviderFactory>()
       .As<IExternalUserInformationProviderFactory>(); 

Upvotes: 1

Related Questions