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 来管理 MainClass
和 SubClass
之间的交互。
接口
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作者
我正在尝试解决一个 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 来管理 MainClass
和 SubClass
之间的交互。
接口
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作者