StructureMap: creating as transitional (on demand) does not work

I am trying to solve an IoC problem that at first seemed easy, but turned out to be a pain in the ass: -P

I have a main heavy weight class that needs to be initialized only once, so it is marked as Singleton. However, this class uses a subclass that must be created once for each request, so it is marked as 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(); // ... } } 

As you can see, I pass lambda to the MainClass constructor, which is used to get the ISubClass instance. During debugging, I could definitely see that ctx.GetInstance<ISubClass>() is executed every time MainClass needs an instance of SubClass . But, to my surprise, SubClass is created only once, as a single, instead of creating for each request.

However, when I call container.GetInstance<ISubClass>() directly from somewhere inside my code, the behavior is exactly what I wanted. SubClass is created once and only once for each request.

I'm not quite sure, but I think the problem arises from the context object that is passed the lambda, which is the singleton (?) Context. But I really don't know how to get the desired behavior here!

I hope you help me with this. Thank you for your responses.

Regards, Dante

+3
c # dependency-injection ioc-container structuremap
source share
3 answers

It seems you need a little redesign. To avoid inactive dependencies , you need to give the root object of the graph a shorter (or equal) lifetime than any of its dependencies.

One option would be to make a 3rd class to control the interaction between MainClass and SubClass .

Interfaces

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

Classes

 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 { } 

Registry

 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>(); } } 
0
source share

This is very normal.

Because you allow ISubclass in the main class, which is singleton.

When the main class is resolved, it will receive one subclass, and they will live together (they must because main is singlenton).

When you allow, you should check the lifetime of the first parent, if it does not matter, you have a request or for dependency, it will live with singleton.

0
source share

If you really need MainClass for a long-lived object, but use a new SubClass object for each request, you can:

1.) Register your own Func instance in the container or register MainClass itself, which delegates to the root container itself. Remember that you can also use automatic wiring, so you do not need to do all this inside.

2.) Add the IContainer itself to MainClass and use it as a service locator to lazily get a new ISubClass during each request.

Using IContext the way you are will not work, because IContext is bound to the original request. โ€œTransientโ€ in StructureMap really means โ€œfor every request,โ€ so when you type Func into your singleton, which delegates the original IContext, you will get the same ISubClass every time. See http://structuremap.imtqy.com/object-lifecycle/ for more information on life cycles.

I am the author of SM btw.

0
source share

All Articles