简单注入器中的工厂接口

mag*_*gol 18 c# dependency-injection simple-injector

我是Ninject用户,试图学习Simple Injector

我经常在我的应用程序中使用的一个Ninject feture是Factory Interface

有了它,我可以创建一个这样的接口:

public interface IBarFactory
{
   Bar CreateBar();
}
Run Code Online (Sandbox Code Playgroud)

并注册就像这样

kernel.Bind<IBarFactory>().ToFactory();
Run Code Online (Sandbox Code Playgroud)

然后我简单可以使用IBarFactory,而不必创建IBarFactory的实现

我现在尝试在Simple njector中找到类似的东西,并找到了这个.但是随着那个接近,我必须实现工厂接口(更多代码).如果Bar对象需要引用另一个对象,该怎么办?

Ste*_*ven 29

Simple Injector缺少这种工厂接口设施.这种省略背后的想法是,在正确应用依赖注入时,最小化了使用工厂的需要,这使得这种特征的有用性受到限制.

在Simple Injector中,您必须自己编写一个实现,但这通常是微不足道的.例:

private sealed class SimpleInjectorBarFactory : IBarFactory {
    private readonly Container container; 

    public SimpleInjectorBarFactory(Container container) {
        this.container = container;
    }

    public Bar CreateBar() =>
        this.container.GetInstance<Bar>();
}
Run Code Online (Sandbox Code Playgroud)

这个类可以像这样注册:

container.RegisterSingleton<IBarFactory, SimpleInjectorBarFactory>();
Run Code Online (Sandbox Code Playgroud)

或者 - 如果你很懒 - 你可以注册注册Func<Bar>如下:

container.RegisterInstance<Func<Bar>>(() => container.GetInstance<Bar>());
Run Code Online (Sandbox Code Playgroud)

请注意,由于此SimpleInjectorBarFactory实现取决于Container实例,因此它应该是组合根的一部分,以防止将Container用作服务定位器.通过将类放在组合根中,它只是一个基础结构.

因此,故意排除该功能,但可以使用有限数量的代码轻松扩展库以允许此功能:

using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Proxies;

public static class AutomaticFactoryExtensions {
    public static void RegisterFactory<TFactory>(this Container container) {
        if (!typeof(TFactory).IsInterface)
            throw new ArgumentException(typeof(TFactory).Name + " is no interface");

        container.ResolveUnregisteredType += (s, e) => {
            if (e.UnregisteredServiceType == typeof(TFactory)) {
                e.Register(Expression.Constant(
                    value: CreateFactory(typeof(TFactory), container),
                    type: typeof(TFactory)));
            }
        };
    }

    private static object CreateFactory(Type factoryType, Container container) {
        var proxy = new AutomaticFactoryProxy(factoryType, container);
        return proxy.GetTransparentProxy();
    }

    private sealed class AutomaticFactoryProxy : RealProxy {
        private readonly Type factoryType;
        private readonly Container container;

        public AutomaticFactoryProxy(Type factoryType, Container container)
            : base(factoryType) {
            this.factoryType = factoryType;
            this.container = container;
        }

        public override IMessage Invoke(IMessage msg) {
            if (msg is IMethodCallMessage) {
                return this.InvokeFactory(msg as IMethodCallMessage);
            }

            return msg;
        }

        private IMessage InvokeFactory(IMethodCallMessage msg) {
            if (msg.MethodName == "GetType")
                return new ReturnMessage(this.factoryType, null, 0, null, msg);

            if (msg.MethodName == "ToString")
                return new ReturnMessage(this.factoryType.Name, null, 0, null, msg);

            var method = (MethodInfo)msg.MethodBase;
            object instance = this.container.GetInstance(method.ReturnType);
            return new ReturnMessage(instance, null, 0, null, msg);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

使用上面的扩展方法,您可以以与Ninject注册非常类似的方式为工厂注册:

container.RegisterFactory<IBarFactory>();
Run Code Online (Sandbox Code Playgroud)

而已.