【问题标题】:Why Simple Injector will not dispose instances resolved by a Func?为什么 Simple Injector 不会处理由 Func 解析的实例?
【发布时间】:2016-11-17 11:39:36
【问题描述】:

我正在尝试按照 Simple Injector 网站上 How To 部分中的建议通过密钥解析实例。

我使用基于字典的工厂。此 dict 将包含对 DI 容器的 Func 引用。当应该创建一个实例时,将询问 DI 容器。在原始代码中,工厂是使用 new() 运算符创建的。我将其更改为让 DI 容器自动处理工厂。 (如果有其他方法可以实现这一点,请现在告诉我。)

        var diContainer = new Container();

        //diContainer.RegisterSingleton<IBasicFactory>(new BasicFactory
        //{
        //    { "A", () => diContainer.GetInstance<A>() },
        //    { "B", () => diContainer.GetInstance<B>() },
        //});

        diContainer.RegisterSingleton<IBasicFactory, BasicFactory>();
        var instance = (BasicFactory) diContainer.GetInstance<IBasicFactory>();
        instance.Add("A", () => diContainer.GetInstance<A>());
        instance.Add("B", () => diContainer.GetInstance<B>());
        diContainer.Verify();

        var factory = diContainer.GetInstance<IBasicFactory>();
        factory.CreateInstance("A").SayHello();
        factory.CreateInstance("B").SayHello();
        diContainer.Dispose();

在实例上的创建运行良好,但工厂返回的那些(A 和 B)中的任何一个都不会在 DI 容器被释放时被释放。

我做错了什么?


下面是其他代码:

using System;
using System.Collections.Generic;

public interface IBasic
{
    void SayHello();
}

public abstract class Basic : IBasic, IDisposable
{
    protected Basic()
    {
        System.Console.WriteLine("Creating instance of Basic");
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if(disposing)
            System.Console.WriteLine("Disposing instance of Basic");
    }

    public abstract void SayHello();
}

public interface IBasicFactory
{
    IBasic CreateInstance(string key);
}

public class BasicFactory : Dictionary<string, Func<IBasic>>, IBasicFactory, IDisposable
{
    public BasicFactory()
    {
        System.Console.WriteLine("Creating instance of BasicFactory");
    }

    public IBasic CreateInstance(string key)
    {
        Func<IBasic> createObject;
        if (this.TryGetValue(key, out createObject))
            return createObject();

        var msg = $"The parameter ${key} is not supported by this factory";
        System.Console.WriteLine(msg);
        throw new NotSupportedException(msg);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            System.Console.WriteLine("Disposing instance of BasicFactory");
            this.Clear();
        }
    }
}

public class A : Basic
{
    public override void SayHello()
    {
        System.Console.WriteLine("Hello A!");
    }
}

public class B : Basic
{
    public override void SayHello()
    {
        System.Console.WriteLine("Hello B!");
    }
}

【问题讨论】:

标签: c# simple-injector


【解决方案1】:

当您在处置容器之前在演示应用程序中添加对.Verify() 的额外调用时,您可以看到您做错了什么:

factory.CreateInstance("A").SayHello();
factory.CreateInstance("B").SayHello();
diContainer.Verify();
diContainer.Dispose();

此验证调用将失败,并出现解释您所做的一切错误的异常:-)

您犯的错误是您没有在容器中显式注册根组件ABAB 被视为根组件,因为您直接从容器中解析它们(使用 GetInstance&lt;T&gt;),而不是将它们注入到另一个组件中。

显式注册根组件很重要,因为它允许 Simple Injector 以可靠的方式analyse your complete object graph

因为在您调用Verify 期间,Simple Injector 不知道AB 的存在,所以它无法警告您registered a disposable component as transient. Simple Injector 不跟踪瞬态组件。如果您需要处置,则必须将它们注册为作用域。

文档中给出的关于那些命名工厂的建议实际上过于简单,并且错过了关于显式注册根组件的警告。我的建议是使用类似于文档中RequestHandlerFactory 示例的构造,因为该示例正确注册了所有类型,从而可以成功验证您的配置:

public class BasicFactory : IBasicFactory {
    private readonly Dictionary<string, InstanceProducer> producers =
        new Dictionary<string, InstanceProducer>(StringComparer.OrdinalIgnoreCase);

    private readonly Container container;

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

    Basic IBasicFactory.CreateNew(string name) => (Basic)this.producers[name].GetInstance();

    public void Register<TImplementation>(string name, Lifestyle lifestyle = null)
        where TImplementation : class, Basic {
        lifestyle = lifestyle ?? Lifestyle.Transient;
        var producer = lifestyle.CreateProducer<Basic, TImplementation>(container);
        this.producers.Add(name, producer);
    }
}

例子:

var factory = new BasicFactory(container);
factory.Register<A>("A", Lifestyle.Scoped);
factory.Register<B>("B", Lifestyle.Scoped);

container.RegisterSingleton<IBasicFactory>(factory)

【讨论】:

  • 谢谢。我不知道瞬态和一次性之间的不匹配。
  • 完美运行 - 即使将其转换为通用 构造 - 谢谢!
  • @al-bex 不要忘记文档中的注释:“注意:密钥注册的需要可能表明应用程序设计不明确,并且表明违反了 Liskov 替换原则. 仔细看看每个键控注册是否不应该有自己独特的接口,或者每个注册都应该实现自己的通用接口版本。”
【解决方案2】:

您的容器不存储它创建的对象,只存储负责创建它的方法。

我可以尝试提出一个解决方案,但我不认为将容器的处置过程与它创建的实例(在这种情况下,由容器创建的工厂创建)相关联是一个很好的模式。想象一下,如果您在另一个线程上使用容器,并且您处置了一个实例,该实例正被其他一些不相关的进程使用。我会单独处理处置过程。

【讨论】:

  • 感谢您的解释。我想这也适用于我必须直接注入容器的实例,因为它们有多个构造函数。我可以在工厂中保留引用并处理它们,但我不喜欢在不同的地方处理组件的处理,只是因为我不能告诉 Simple Injector“为你服务的所有组件做这件事”。有没有替代的 DI 容器可以处理这个问题?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多