Autofac - 通过工厂方法解决

Kc_*_*Kc_ 5 c# ioc-container factory-method autofac interceptor

我似乎无法在 Autofac 中解决这种情况:

  • 我的业务逻辑需要一个 IFinal 类型的对象。
  • 实现类 Final 有几个业务逻辑不知道的依赖,但可以通过 IoCContainer 直接解析。
  • Final 还具有其他依赖项,这些依赖项仅在运行时由业务逻辑知道。
  • 我希望能够在 IFinal 上使用拦截器,因此应通过 IFinal 注册和解析类 Final。

所以我试图为 IFinal 创建一个工厂,但我似乎无法得到正确的解决方案。

示例代码:

    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);
    }
}
Run Code Online (Sandbox Code Playgroud)

Google 和 Autofac 文档对此无能为力。
不知怎的,我的脑子里似乎有一个结,也许我只是尝试使用错误的模式。
我很确定有一个解决方案,但我找不到它。

t3c*_*b0t 5

您必须将工厂 添加delegateFinal

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

    public Final(IDependency dependency, int runtimeArgument) { }

    public void Test() { }
}
Run Code Online (Sandbox Code Playgroud)

Autofac 足够聪明,可以填充其他参数,因此您的工厂委托只能具有您在运行时指定的参数,而其他参数则由魔法解析。

然后你注册类型:

    builder
        .RegisterType<Final>()
        .As<IFinal>();
Run Code Online (Sandbox Code Playgroud)

这是完整的示例:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

你根本不需要这Factory门课。委托可以直接从scope任何其他依赖项解析或以与任何其他依赖项相同的方式使用,并由 Autofac 注入。只是Final.Factory作为你的依赖。

该文档在委托工厂中描述了此技术


Kc_*_*Kc_ 3

“提出正确的问题,你就会找到正确的答案”的一个很好的例子。
我已经尝试让它工作 2 天了,在我在这里发布问题后 1 小时,我自己找到了答案:P

这是包括拦截器的工作代码:

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

        builder.RegisterType<Interceptor>();

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

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

        builder.Register<IFinal>((c, p) =>
        {
            IDependency dependency = c.Resolve<IDependency>();
            int runtimeArgument = p.Named<int>("runtimeArgument");
            return new Final(dependency, runtimeArgument);
        }).As<IFinal>()
       .EnableInterfaceInterceptors()
       .InterceptedBy(typeof(Interceptor));

        builder.Register<Factory>((c,p)=>{
            Factory.FactoryMethod finalObjectFactoryMethod = c.Resolve<Factory.FactoryMethod>();
            return new Factory(finalObjectFactoryMethod);
        }).As<IFactory>()
       .EnableInterfaceInterceptors()
       .InterceptedBy(typeof(Interceptor));

        IContainer container = builder.Build();


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


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
{
    public delegate IFinal FactoryMethod(int runtimeArgument);

    readonly FactoryMethod _finalObjectFactoryMethod;

    public Factory(FactoryMethod finalObjectFactoryMethod)
    {
        _finalObjectFactoryMethod = finalObjectFactoryMethod;
    }

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

public class Interceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        Console.WriteLine($"call {invocation.Method.Name}");
        invocation.Proceed();
        Console.WriteLine($"return from {invocation.Method.Name}");
    }
}
Run Code Online (Sandbox Code Playgroud)

拦截器现在将其写入控制台:

call GetFinalObject
return from GetFinalObject  
call Test  
return from Test  
Run Code Online (Sandbox Code Playgroud)

也许这可以帮助其他人。