StructureMap:创建为瞬态(根据请求)不起作用

StructureMap: Creation as Transient (per request) not working

我正在尝试解决一个 IoC 问题,一开始看起来很容易,但结果却很麻烦:-P

我有一个重量级的main class,它必须只初始化一次,所以它被标记为Singleton。但是这个 class 使用了一个子 class ,它必须为每个请求创建一次,所以它被标记为 Transient:

public class MyRegistry : Registry
{
    public MyRegistry()
    {
        For<IMainClass>()
            .Singleton()
            .Use(ctx => new MainClass(() => ctx.GetInstance<ISubClass>()));

        For<ISubClass>()
            .Transient()
            .Use(ctx => CreateNewInstanceOfSubClass());
    }

    private ISubClass CreateNewInstanceOfSubClass()
    {
        return new SubClass();
    }
}

public interface ISubClass
{ }

public class SubClass : ISubClass
{ }

public interface IMainClass
{ }

public class MainClass : IMainClass
{
    private readonly Func<ISubClass> _subClassProvider;

    public MainClass(Func<ISubClass> subClassProvider)
    {
        _subClassProvider = subClassProvider;
    }

    public void DoStuff()
    {
        var requestSpecificInstanceOfSubClass = _subClassProvider();

        // ...
    }
}

如您所见,我向 MainClass 的构造函数传递了一个 lambda,用于获取 ISubClass 的实例。在调试过程中,我可以肯定地看到每次 MainClass 需要一个 SubClass 实例时都会执行 ctx.GetInstance<ISubClass>()。但令我惊讶的是,SubClass 仅作为单例创建一次,而不是为每个请求创建。

但是,当我直接从代码中的某处调用 container.GetInstance<ISubClass>() 时,行为完全符合预期。 SubClass 只为每个请求创建一次。

我不太确定,但我想问题出在传递给 lambda 的上下文对象上,它是单例(?)的上下文。但是我真的不知道如何在这里获得所需的行为!?

我希望你能帮我解决这个问题。感谢您的回答。

此致, 但丁

这很正常。

因为您正在解析 Main class 中的 ISubclass,它是单例。

当 Main Class 解决后,它会得到一个 Subclass 并且他们将住在一起(他们必须住在一起,因为 main 是单身人士)。

当你决定你应该检查第一个父生命周期是否是单例时,无论你有每个请求还是每个依赖,它都会与单例一起生活。

看来你需要稍微修改一下设计。为避免 captive dependencies,您需要为图形的根对象提供比其任何依赖项更短(或相等)的生命周期。

一个选择是创建第 3 个 class 来管理 MainClassSubClass 之间的交互。

接口

public interface IInteractionManager
{ 
    void DoStuff();
}

public interface IMainClass
{ 
    void DoStuff(ISubclass subclass);
}

public interface ISubClass
{ }

public class InteractionManager : IInteractionManager
{
    private readonly IMainClass mainClass;
    private readonly ISubClass subClass;

    public InteractionManager(
        IMainClass mainClass,
        ISubClass subClass)
    {
        this.mainClass = mainClass;
        this.subClass = subClass;
    }

    public void DoStuff()
    {
        this.mainClass.DoStuff(this.subClass);
    }
}

public class MainClass : IMainClass
{
    public void DoStuff(ISubclass subclass)
    {
        // do something with transient subclass
    }
}

public class SubClass : ISubClass
{ }

注册表

public class MyRegistry : Registry
{
    public MyRegistry()
    {
        // The root of the object graph is transient...
        For<IInteractionManager>()
            .Transient()
            .Use<InteractionManager>();

        // ...therefore, it can have transient dependencies...
        For<ISubClass>()
            .Transient()
            .Use<SubClass>();

        // ...and singleton dependencies.
        For<IMainClass>()
            .Singleton()
            .Use<MainClass>();
    }
}

如果您确实需要 MainClass 成为一个长期存在的对象,但每个请求都使用一个新的 SubClass 对象,您可以:

1.) 将 Func 的自定义实例注册到容器,或注册 MainClass 本身,委托给根 Container 本身。请记住,您也可以使用自动连接,这样您就不必全部内联完成。

2.) 将 IContainer 本身注入 MainClass 并将其用作服务定位器,以便在每次请求期间延迟检索新的 ISubClass。

按照您现在的方式使用 IContext 是行不通的,因为 IContext 与原始请求相关联。 "Transient" 在 StructureMap 中的真正含义是 "per request," 因此,当您将 Func 注入委托给原始 IContext 的单例中时,您每次都会获得完全相同的 ISubClass。有关生命周期的更多信息,请参阅 http://structuremap.github.io/object-lifecycle/

顺便说一下我是SM作者