Simple Injector Prevent Locked Container

一曲冷凌霜 提交于 2019-12-11 03:14:45

问题


I need to know if there is a way to prevent the container from locking after calling GetInstance?

I have an implementation INamedInstanceFactory<T> where T : Xamarin.Forms.Page, and I need to have an association between a string and a specific concrete type. Unfortunately in order to make it work I have to resolve INamedInstanceFactory<Page> before all registrations are complete.

How can I prevent the container from being locked?

Edit: We start with an extension like:

public static void RegisterPage<T>(this Container container, string name)
    where T : Page
{
   // Do Platform stuff
   var namedInstance = continer.GetInstance<INamedInstanceFactory<Page>>();
   var namedInstance.Register( typeof( T ), name, Lifestyle.Transient );
}

Note: this sample uses a transient lifestyle but other services may require a singleton.

public class NamedInstanceFactory<T> : Dictionary<string, Type>, INamedInstanceFactory<T>
{
    Container _container { get; }
    public NamedInstanceFactory(Container container)
    {
        _container = container;
    }

    public virtual T CreateNew(string name)
    {
        if( ContainsKey(name))
            return (T)_container.GetInstance(this[name]);
        return default(T);
    }

    public virtual void RegisterType(Type type, string name, Lifestyle lifestyle = null)
    {
        if (type == null) throw new Exception(
            $"The NamedInstanceFactory<{typeof(T).Name}> cannot register null types");

        if (string.IsNullOrWhiteSpace(name))
        {
            name = type.Name;
        }

        if (lifestyle == null)
        {
            lifestyle = Lifestyle.Transient;
        }

        this[name] = type;
        _container.Register(type, type, lifestyle);
    }
}

And of course INamedInstanceFactory<> is registered as:

Container.Register(typeof(INamedInstanceFactory<>), typeof(NamedInstanceFactory<> ), 
    Lifestyle.Singleton);

Keeping with the page example. We might have a NavigationStack expressed as MainPage/PageA/PageB/PageC and we need to be able to parse that and resolve the actual page type for the key "MainPage" or "PageA" etc so to create the page object we would then have a function like:

public Page CreatePage(string name)
{
    return namedInstanceFactory.CreateNew(name);
}

回答1:


Simple Injector locks the container after first use, and it does not allow the container to be unlocked. This behaviour is very deliberate and it is explained in detail here why making registrations after having resolved is a bad idea. Making registrations after you resolve is an anti-pattern that is sometimes referred to as Register Resolve Register (not to confuse with Register Resolve Release).

While Simple Injector had this design from day one, other DI Container maintainers realized this as well and you see more DI Containers (like Autofac and Ninject) move into a strict model where the container can't be changed after either the container is built or when a first resolve has been made.

Simple Injector is very strict about this, because -not only causes the Register-Resolve-Register pattern more harm than good, it is important to realize that there is always a cleaner way to design and write your code, in such way that it becomes unneeded to apply Register-Resolve-Register. This however often requires somewhat detailed information of what it is you’re trying to accomplish, which is why I insisted in getting more information.

One of the reasons that developers get lured into the Register-Resolve-Register anti-pattern, is because they are misled into thinking that it is a sin to create classes using new and that only the Container is allowed to create instances.

This thinking however is false. Although it can be beneficial to let the container Auto-Wire types for you, as long as Components (the classes of your application that contain behaviour) are created inside the Composition Root, it doesn’t really matter whether a container creates them, or whether you do it by hand (a.k.a. Pure DI).

In some cases it even makes sense to create object or parts of the object graph manually and in some cases this even leads to more maintainable code, because Containers are especially suited to build large sets of objects that are composed into simple object graphs. When an object graph gets a complicated structure, this can tremendously complicate your DI configuration, and in that case it can really help to fall back to Pure DI for that particular part of the object graph. But I digress.

In your case, since the NamedInstanceFactory<Page> factory is a simple singleton. It only references the Container which is a singleton as well, and it’s very unlikely such factory would get other dependencies as well or should have a different lifestyle. This means there is no need to resolve it from the container (although in theory you still could without causing Register-Resolve-Register, but let’s not do that), even though this type is generic.

If I’m not mistaken, the following registration would effectively solve your problem:

var pageFactory = new NamedInstanceFactory<Page>(Container);
Container.RegisterSingleton<INamedInstanceFactory<Page>>(pageFactory);

pageFactory.RegisterType(typeof(StartPage), Lifestyle.Singleton);
pageFactory.RegisterType(typeof(UserDetailsPage), Lifestyle.Transient);
pageFactory.RegisterType(typeof(SomeOtherPage), Lifestyle.Transient);

Although it is possible to let the container Auto-Wire the NamedInstanceFactory<T> (even by using the open-generic registration), I seriously doubt that such a thing would actually make the solution simpler than the one presented here.

In case you’re interested, the documentation link provided earlier contains a paragraph at the bottom that explains how to add registrations in a delayed fashion.



来源:https://stackoverflow.com/questions/42516913/simple-injector-prevent-locked-container

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!