简体   繁体   中英

Factory Interface in Simple Injector

I'm a Ninject user that try to learn Simple Injector

One Ninject feture that I often use in my applications is the Factory Interface

With that I can create a Interface like this:

public interface IBarFactory
{
   Bar CreateBar();
}

And the register it like this

kernel.Bind<IBarFactory>().ToFactory();

Then I simple can use IBarFactory, and don't have to create a implementation of IBarFactory

I now try to find anything similar in Simple njector, and have found this . But with that approacher, I have to implement the factory interface (more code). And how do I do if the Bar object need a reference to another object?

Simple Injector lacks this a factory interface facility. The idea behind this omission is that when applying Dependency Injection correctly, the need for using factories is minimized , which makes the usefulness of such feature limited.

In Simple Injector you have to write an implementation yourself, but this is usually trivial. Example:

private sealed class SimpleInjectorBarFactory : IBarFactory
{
    private readonly Container container; 
    public SimpleInjectorBarFactory(Container container) => this.container = container;
    public Bar CreateBar() => this.container.GetInstance<Bar>();
}

This class can be registered like this:

container.RegisterSingleton<IBarFactory, SimpleInjectorBarFactory>();

Or -if you're lazy- you can register a Func<Bar> to be injected as follows:

container.RegisterInstance<Func<Bar>>(() => container.GetInstance<Bar>());

Note that since this SimpleInjectorBarFactory implementation depends on the Container instance, it should be part of the Composition Root to prevent using the Container as a Service Locator . By placing the classes inside your Composition Root it becomes merely a piece of infrastructure .

So the feature is excluded deliberately, but the library can be extended by making use of the ResolveUnregisteredType event:

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

Using the extension method above, you can do the registration for the factory in a way very similar to Ninject's registration:

container.RegisterFactory<IBarFactory>();

That's it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM