与 StructureMap 4.6 瞬态生命周期的混淆

Nav*_*d K 3 c# structuremap dependency-injection inversion-of-control

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

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

程序.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();
    }
}
Run Code Online (Sandbox Code Playgroud)

控制台注册表

public class ConsoleRegistry : Registry
{
    public ConsoleRegistry()
    {
        Scan(_ =>
        {
            _.TheCallingAssembly();
            _.WithDefaultConventions();
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

文件服务

public interface IFileService
{
    string Read(string path);

    void Write(string path, string content);

    bool FileExists(string path);

    string GetUniqueID();
}
Run Code Online (Sandbox Code Playgroud)

文件服务.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);
    }
}
Run Code Online (Sandbox Code Playgroud)

当我运行应用程序时,结果是:

输出

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

Nig*_*888 5

您对文档的理解不正确。

  • Transient——默认生命周期。为每个逻辑请求创建一个新对象,以从容器解析对象图。
  • 单例——只会为容器创建一个对象实例,以及由该容器创建的所有子容器或嵌套容器

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

但是您所描述的行为是针对Singleton 的,这意味着仅在第一次Resolve调用时创建实例。

要获得您想要的行为,您必须将注册类型更改为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);
    }
}
Run Code Online (Sandbox Code Playgroud)

参考:如何将 Structuremap 配置为通过 Singleton 在 Assembly 和 Cache 中自动扫描类型?

单例 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; } }
}
Run Code Online (Sandbox Code Playgroud)

容器

在我们的容器中,我们注册了 2 个实例Service,一个是单例,一个是瞬态。每个容器实例只实例化一次每次Resolve调用时都会实例化瞬态。

public class MyContainer
{
    private readonly Service singleton = new Service();

    public Application Resolve()
    {
        return new Application(
            singleton: this.singleton, 
            transient: new Service());
    }
}
Run Code Online (Sandbox Code Playgroud)

用法

在现实世界的应用程序中,只有一个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();
    }
}
Run Code Online (Sandbox Code Playgroud)

输出

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