与StructureMap 4.6瞬态生命周期混淆

时间:2018-03-07 16:23:20

标签: c# dependency-injection inversion-of-control structuremap

我使用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);
    }
}

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

Output

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

1 个答案:

答案 0 :(得分:3)

您对documentation的理解不正确。

  
      
  • 瞬态 - 默认生命周期。为每个逻辑请求创建一个新对象,以便从容器中解析对象图。
  •   
  • Singleton - 只为容器和该容器创建的任何子容器或嵌套容器创建一个对象实例
  •   

您正在使用 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);
    }
}

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

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