简单的注射器防止锁定的容器

时间:2017-02-28 19:11:02

标签: c# dependency-injection simple-injector

我需要知道在调用GetInstance后是否有办法阻止容器锁定?

我有一个实现INamedInstanceFactory<T>,其中T:Xamarin.Forms.Page,我需要在字符串和特定的具体类型之间建立关联。不幸的是,为了使其工作,我必须在所有注册完成之前解决INamedInstanceFactory<Page>

如何防止容器被锁定?

编辑: 我们从像:

这样的扩展开始
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 );
}

注意:此示例使用短暂的生活方式,但其他服务可能需要单身。

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

当然是INamedInstanceFactory&lt;&gt;注册为:

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

保持页面示例。我们可能有一个表示为MainPage/PageA/PageB/PageC的NavigationStack,我们需要能够解析它并解析密钥“MainPage”或“PageA”等的实际页面类型,以便创建页面对象,然后我们将拥有一个函数像:

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

1 个答案:

答案 0 :(得分:1)

Simple Injector在首次使用后锁定容器,并且不允许容器解锁。这种行为是非常慎重的,详细解释here为什么在解决后进行注册是一个坏主意。解决后进行注册是一种反模式,有时称为注册解析注册(不要与 Register Resolve Release 混淆)。

虽然Simple Injector从第一天起就有这个设计,但其他DI Container维护者也意识到这一点,你会看到更多DI容器(如AutofacNinject在构建容器或完成第一次解析后,进入严格的模型,其中容器无法更改

Simple Injector对此非常严格,因为 - 不仅仅会导致Register-Resolve-Register模式弊大于利,重要的是要认识到始终是一种更简洁的设计方式以这样的方式编写代码,使得应用Register-Resolve-Register变得不必要。然而,这往往需要一些关于你想要完成什么的详细信息,这就是为什么我坚持要获得更多信息。

开发人员被引入Register-Resolve-Register反模式的原因之一是因为他们被误导认为使用new创建类是犯罪并且只允许使用Container创建实例。

然而,这种想法是错误的。虽然让容器 Auto-Wire 类型为您提供帮助是有益的,只要组件(包含行为的应用程序类) )是在Composition Root内创建的,无论容器是否创建它们,或者你是否手工完成(aka Pure DI)都无关紧要。

在某些情况下,手动创建对象图形的对象或部分甚至是有意义的,在某些情况下甚至可以导致更易于维护的代码,因为Container特别适合构建组成简单对象图的大型对象集。当一个对象图得到一个复杂的结构时,这会极大地使你的DI配置变得复杂,在这种情况下,对于对象图的特定部分,它真的有助于回退到Pure DI。但我离题了。

在您的情况下,由于NamedInstanceFactory<Page>工厂是一个简单的单身人士。它只引用Container这也是一个单身人士,而且这样的工厂也不太可能得到其他依赖关系,或者应该有不同的生活方式。这意味着不需要从容器中解析它(虽然理论上你仍然可以在不引起Register-Resolve-Register的情况下,但是我们不这样做),即使这种类型是通用的。

如果我没有记错的话,以下注册将有效解决您的问题:

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

尽管可以让容器自动连接NamedInstanceFactory<T>(即使使用开放式通用注册),但我非常怀疑这样的事情实际上会使解决方案比这里介绍的更简单。

如果您感兴趣,前面提供的documentation link在底部包含一个段落,说明如何以延迟方式添加注册。