Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Autofac - resolving through factory methods

I just can't seem to wrap my brain around this situation in Autofac:

  • My business logic needs an object of type IFinal.
  • The implementing class Final has several dependencies that are not known by the business logic, but can be directly resolved via the IoCContainer.
  • Final also has other dependencies that are only known by the business logic at run time.
  • I want to be able to use Interceptors on IFinal, so class Final should be registered and resolved via IFinal.

So I tried to create a factory for IFinal, but I can't seem to get the resolution right.

Example code:

    public static void Main(string[] args)
    {
        ContainerBuilder builder = new ContainerBuilder();

        builder.RegisterType<Dependency>().As<IDependency>();

        builder.RegisterType<Final>().As<IFinal>();

        // this fails because context c is not available at the time finalObjectFactoryMethod is used
        builder.Register<Factory>((c,p)=>{
            var dependency = c.Resolve<IDependency>();
            Func<int, IFinal> finalObjectFactoryMethod =
                rta => c.Resolve<IFinal>(
                    new TypedParameter(typeof(IDependency), dependency),
                    new TypedParameter(typeof(int), rta)
                );
            return new Factory(finalObjectFactoryMethod);
        }).As<IFactory>();

        // this works but doesn't use the resolve mechanism for IFinal
        /*
        builder.Register<Factory>((c,p)=>{
            var dependency = c.Resolve<IDependency>();
            Func<int, IFinal> finalObjectFactoryMethod = rta => new Final(dependency, rta);
            return new Factory(finalObjectFactoryMethod);
        }).As<IFactory>();
        */

        IContainer container = builder.Build();


        IFactory factory = container.Resolve<IFactory>();
        IFinal final = factory.GetFinalObject(42);
    }
}


public interface IDependency{}

public class Dependency: IDependency{}


public interface IFinal
{
    void Test();
}

public class Final: IFinal
{
    public Final(IDependency dependency, int runtimeArgument){}

    public void Test(){}
}


public interface IFactory
{
    IFinal GetFinalObject(int runtimeArgument);
}

public class Factory: IFactory
{
    readonly Func<int, IFinal> _finalObjectFactoryMethod;

    public Factory(Func<int, IFinal> finalObjectFactoryMethod)
    {
        _finalObjectFactoryMethod = finalObjectFactoryMethod;
    }

    public IFinal GetFinalObject(int runtimeArgument)
    {
        return _finalObjectFactoryMethod(runtimeArgument);
    }
}

Google and Autofac documentation could not help me on this.
Somehow there seems to be a knot in my brain, maybe I just try to use the wrong pattern.
I'm pretty sure there is a solution for this, but I just can't find it.

like image 320
Kc_ Avatar asked Apr 01 '18 05:04

Kc_


1 Answers

You have to add a factory delegate to Final:

public class Final : IFinal
{
    public delegate IFinal Factory(int runtimeArgument);

    public Final(IDependency dependency, int runtimeArgument) { }

    public void Test() { }
}

Autofac is clever enough to fill the other parameters in so your factory delegate can only have parameters you specify at runtime and the other ones are resolved by magic.

Then you register the type:

    builder
        .RegisterType<Final>()
        .As<IFinal>();

Here's the complete example:

public static void Main(string[] args)
{
    ContainerBuilder builder = new ContainerBuilder();

    builder
        .RegisterType<Dependency>()
        .As<IDependency>();

    builder
        .RegisterType<Final>()
        .As<IFinal>();

    using(var container = builder.Build())
    using (var scope = container.BeginLifetimeScope())
    {

        var finalFactory = scope.Resolve<Final.Factory>();
        IFinal final = finalFactory(42);
    }
}

You don't need the Factory class at all. The delegate can either be resolved directly from scope or used the same way as any other dependency and injected by Autofac. Just juse Final.Factory as your dependency.

The documentation describes this technique in the Delegate Factories

like image 110
t3chb0t Avatar answered Sep 26 '22 15:09

t3chb0t