【问题标题】:Confusion with StructureMap 4.6 Transient Lifecycle与 StructureMap 4.6 瞬态生命周期的混淆
【发布时间】:2018-03-07 16:23:19
【问题描述】:

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

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

程序.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();
}

文件服务.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 实例。但是,正如您所看到的,它提供了两个不同的实例。为什么会这样?

【问题讨论】:

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


    【解决方案1】:

    你对documentation的理解有误。

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

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

    但您所描述的行为是针对单例,这意味着仅在第一次调用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?

    单例 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

    【讨论】:

    • 谢谢,但由于文档中的这一行,我有点困惑:'考虑 Transient 的最简单方法是为每个对 Container 的顶级调用创建一个对象实例。 GetInstance()(或 IContainer 接口上的任何其他对象解析方法)。你能给我解释一下吗?这和 Singletone 有什么区别?
    • 我在答案中添加了一个示例。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-22
    • 1970-01-01
    相关资源
    最近更新 更多