【问题标题】:Autofac - resolving through factory methodsAutofac - 通过工厂方法解析
【发布时间】:2018-09-10 17:18:38
【问题描述】:

我似乎无法在 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);
    }
}

Google 和 Autofac 文档在这方面无法帮助我。
不知何故,我的大脑似乎有一个结,也许我只是尝试使用错误的模式。
我很确定有一个解决方案,但我就是找不到。

【问题讨论】:

    标签: c# autofac ioc-container interceptor factory-method


    【解决方案1】:

    你必须添加一个工厂 delegateFinal

    public class Final : IFinal
    {
        public delegate IFinal Factory(int runtimeArgument);
    
        public Final(IDependency dependency, int runtimeArgument) { }
    
        public void Test() { }
    }
    

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

    然后你注册类型:

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

    这是完整的例子:

    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);
        }
    }
    

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

    文档在Delegate Factories中描述了这种技术

    【讨论】:

    • 我认为我唯一的抱怨是,这会将Final,否则internal 的实现细节(在我的情况下)暴露给外界。在您的示例中,Finalpublic,但可能并不总是希望将接口实现暴露在其程序集之外。
    【解决方案2】:

    “提出正确的问题,您就会找到正确的答案”的绝佳示例。
    在我在这里发布我的问题后 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}");
        }
    }
    

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

    call GetFinalObject
    return from GetFinalObject  
    call Test  
    return from Test  
    

    也许这对其他人有帮助。

    【讨论】:

    • 哦,很高兴知道。我以前从未使用过拦截器。很好,我知道这种感觉,几分钟后你自己问并找到答案;-)
    【解决方案3】:

    我发现其他 cmets 中的解决方案非常不令人满意。这就是为什么我把头绕在这个例子上。也许您或其他人可以从中受益。

    虽然该示例句法正确,但我的假设是从语义的角度来看它是不正确的。

    一些解释: 我更喜欢使用构造函数注入来编写编译时依赖项(就像你和其他人已经做的那样)。另一方面,运行时依赖项是使用方法参数传递的。

    因此,我建议重新设计您的类,将 runtimeArgument 传递给 Test() 方法。对我来说,在早期阶段似乎不需要这个论点。

    public interface IDependency { }
    
    public class Dependency : IDependency { }
    
    public interface IFinal
    {
        void Test(int runtimeArgument);
    }
    
    public class Final : IFinal
    {
        public Final(IDependency dependency) { }
    
        public void Test(int runtimeArgument)
        {
            Console.WriteLine($"Argument is {runtimeArgument}.");
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            var svc = Service;
            svc.Test(42);
        }
    
        private static IFinal Service
        {
            get
            {
                var builder = new ContainerBuilder();
    
                builder.RegisterType<Final>()
                    .As<IFinal>();
    
                builder.RegisterType<Dependency>()
                    .As<IDependency>();
    
                var container = builder.Build();
    
                using var scope = container.BeginLifetimeScope();
                {
                    return scope.Resolve<IFinal>();
                }
            }
        }
    }
    

    【讨论】:

    • 哦,好吧...从那时起已经有一段时间了:D 如果知道运行时参数在哪里使用 Test() 方法,那么您的解决方案就可以工作。如果不是(就像我的情况一样,如果我没记错的话),那么它必须事先附加到对象上。这就是我想在构造函数中初始化它的原因。
    • 如果在调用 Test() 的地方不知道该参数,那么它至少在其他地方是已知的——而这在其他地方就是我将传递给对象的依赖项。这与使参数本身成为依赖项相反。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-04
    • 2021-07-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多