与 StructureMap 4.6 瞬态生命周期混淆

Confusion with StructureMap 4.6 Transient Lifecycle

我正在使用 StructureMap 4.6 作为我的 IoC 容器。我对它的生命周期有点困惑。正如我在其文档中所读到的,Transient 将为每个容器创建一个对象实例。 Supported Lifecycles

我正在通过创建一个简单的控制台应用程序项目来检查这种情况。我的代码如下:

Program.cs

class Program
{
    private static IContainer _Container;
    static void Main(string[] args)
    {
        _Container = Container.For<ConsoleRegistry>();

        var serv1 = _Container.GetInstance<IFileService>();
        Console.WriteLine($"Name: {_Container.Name}");
        Console.WriteLine(serv1.GetUniqueID());

        var serv2 = _Container.GetInstance<IFileService>();
        Console.WriteLine($"Name: {_Container.Name}");
        Console.WriteLine(serv2.GetUniqueID());

        Console.ReadKey();
    }
}

ConsoleRegistry.cs

public class ConsoleRegistry : Registry
{
    public ConsoleRegistry()
    {
        Scan(_ =>
        {
            _.TheCallingAssembly();
            _.WithDefaultConventions();
        });
    }
}

IFileSerivce.cs

public interface IFileService
{
    string Read(string path);

    void Write(string path, string content);

    bool FileExists(string path);

    string GetUniqueID();
}

FileService.cs

public class FileService : IFileService
{
    private static int instanceCounter;
    private readonly int instanceId;

    public FileService()
    {
        this.instanceId = ++instanceCounter;
        Console.WriteLine("File Service is Created.");
    }

    public int UniqueID
    {
        get { return this.instanceId; }
    }

    public string GetUniqueID()
    {
        return UniqueID.ToString();
    }

    public string Read(string path)
    {
        return File.ReadAllText(path);
    }

    public void Write(string path, string content)
    {
        File.WriteAllText(path, content);
    }

    public bool FileExists(string path)
    {
        return File.Exists(path);
    }
}

当我运行申请时结果是:

我的问题是,当我解析 IFileService 的实例时,我希望每个容器获得一个 FileService 的实例。但是,如您所见,它给出了两个不同的实例。为什么会这样?

您对 documentation 的理解不正确。

  • Transient -- The default lifecycle. A new object is created for each logical request to resolve an object graph from the container.
  • Singleton -- Only one object instance will be created for the container and any children or nested containers created by that container

您正在使用 Transient,这意味着每次 Resolve 被调用时您都会得到一个实例

但是您描述的行为是针对 Singleton,这意味着创建实例 仅在第一次 Resolve 被调用时 [=64] =].

要获得您想要的行为,您必须将注册类型更改为 Singleton

public class ConsoleRegistry : Registry
{
    public ConsoleRegistry()
    {
        Scan(_ =>
        {
            _.TheCallingAssembly();
            _.With(new SingletonConvention<IFileService>());
            _.WithDefaultConventions();
        });
    }
}

internal class SingletonConvention<TPluginFamily> : IRegistrationConvention
{
    public void Process(Type type, Registry registry)
    {
        if (!type.IsConcrete() || !type.CanBeCreated() || !type.AllInterfaces().Contains(typeof(TPluginFamily))) return;

        registry.For(typeof(TPluginFamily)).Singleton().Use(type);
    }
}

参考:How can I configure Structuremap to auto scan type in Assembly and Cache by Singleton?

单例 VS 瞬态示例

考虑这一点的最简单方法是展示一个示例而不 使用 DI 容器。

服务

这里我们有一个服务和一个应用服务。这里唯一真正的区别是应用程序服务旨在成为整个应用程序图

public class Service
{
    public string Name { get; private set; } = Guid.NewGuid().ToString();
}

public class Application
{
    private readonly Service singleton;
    private readonly Service transient;

    public Application(Service singleton, Service transient)
    {
        this.singleton = singleton;
        this.transient = transient;
    }

    public Service Singleton { get { return singleton; } }
    public Service Transient { get { return transient; } }
}

容器

在我们的容器中,我们注册了 2 个 Service 实例,一个单例和一个瞬态。每个容器实例 仅实例化单例 一次。每次 Resolve 被调用 .

时都会实例化瞬态
public class MyContainer
{
    private readonly Service singleton = new Service();

    public Application Resolve()
    {
        return new Application(
            singleton: this.singleton, 
            transient: new Service());
    }
}

用法

在现实世界的应用程序中,只有一个 Application 实例。但是,我们展示了两个 Application 实例,以证明注册为 Singleton 的服务将是同一容器实例的同一实例。每次调用 Resolve 时都会创建一个瞬态。

class Program
{
    static void Main(string[] args)
    {
        var container = new MyContainer();

        var application1 = container.Resolve();
        var application2 = container.Resolve();


        Console.WriteLine($"application1.Transient.Name: {application1.Transient.Name}");
        Console.WriteLine($"application2.Transient.Name: {application2.Transient.Name}");
        Console.WriteLine();
        Console.WriteLine($"application1.Singleton.Name: {application1.Singleton.Name}");
        Console.WriteLine($"application2.Singleton.Name: {application2.Singleton.Name}");

        Console.ReadKey();
    }
}

输出

application1.Transient.Name: dc134d4d-75c8-4f6a-a1a5-367156506671 application2.Transient.Name: f3012ea2-4955-4cfa-8257-8e03a00b1e99

application1.Singleton.Name: 86d06d7d-a611-4f57-be98-036464797a41 application2.Singleton.Name: 86d06d7d-a611-4f57-be98-036464797a41