0

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

Community
  • 1
  • 1
Michael McCarthy
  • 1,502
  • 3
  • 18
  • 45

1 Answers1

1

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>(); 
Cyril Durand
  • 15,834
  • 5
  • 54
  • 62
  • "which may avoid issue with scope & co."... what does "co." stand for? Also, as far as my initial solution, how does changing the factory to use IIndex help/better let Autfac control scope? – Michael McCarthy Aug 08 '16 at 12:33
  • 3
    This solution doesn't require `ExternalUserInformationProviderFactory` to have a dependency on `IComponentContext`. [Only your composition root should know about the DI container](http://stackoverflow.com/questions/6277771/what-is-a-composition-root-in-the-context-of-dependency-injection) – Chima Osuji Aug 08 '16 at 13:31
  • Chima, given the above solution, since IIndex is being passed to the factory, the factory does know about Autofac...so I'm not too sure if this colution fulfills the link you provided to "only your composition room should know about the DI container" – Michael McCarthy Aug 08 '16 at 14:16
  • @indiecodemonkey you are right, by having a dependency on `IIndex` your component rely on `Autofac`. By the way, this factory is really small, you can leave it at the composition root level : interface at runtime level and implementation on composition root level. You can also refactor it to make it generic (ie `IFactory`) – Cyril Durand Aug 08 '16 at 15:31
  • @indiecodemonkey I don't like having dependencies on `IComponentContext` when I don't need it. It is really easy to make errors with `IComponentContext` (by creating child lifetimescope, by accessing component registry, etc). BTW If you look at the source code of `KeyedServiceIndex` which is the default implementation of `IIndex` you will see that this component rely on `IComponentContext` :-) – Cyril Durand Aug 08 '16 at 15:35
  • @CyrilDurand. I assume usinig IIndex is a better approach because you're letting Autofac manage IComponentContext instead of injecting IComponentContext and using it's API directly in the factory? – Michael McCarthy Aug 08 '16 at 16:07