Reputation: 22933
We're using StructureMap, and the default life cycle for a container is to create a new object every time that type is asked for. Recently, we started to investigate in creating a nested container for each HTTP request in our web application. It works great, except that the life cycle of a nested container is completely different from a regular container in that all objects becomes singletons inside the nested container.
Since we use StructureMap for most of our object creation, our code breaks in mysterious ways with the life cycle of a nested container. One could argue that this is an error on our part, and I guess that's correct because we call GetInstance()
even when we really expect a new instance to be created. But I can't find any way for us to bypass the life cycle management in StructureMap and force creation (something like a CreateInstance()
instead of GetInstance()
). We could go about implementing our own CreateInstance()
method, but that sort of feels like reinventing StructureMap. We could also change our factories to create objects explicitly, but that doesn't really work when we need to get an instance generically (container.GetInstance<IMyType>()
).
So any advice on forcing StructureMap to create objects, or how to change the life cycle in a nested container, or how to change our factory code to more explicitly creating instances would be great.
Upvotes: 1
Views: 3164
Reputation: 123861
We were in similar situation, talking about this part of your comment under your question:
...We have one website/instance of our web application, and depending on who you are, we connect to the database that you belong to. So we want an isolated container per request to avoid state bleeding over into different systems (User A gets an object from User B's system)...
I was searching for the same feature, which should be multi-thread safe. Then I found this link: StructureMap: Multithreaded env. No default instance defined for PluginFamily
The result could be described this way:
1) Requirement: There is an abstraction in your solution. Instead of StructureMap.ObjectFactory.GetInstance
... all parts should call your
Factory.GetInstance(type)
(and its provider, which will call StructureMap (SM) or later any other IoC provider)
2) if this is the case (or you can introduce e.g. the Manager pattern for your Factory
, and let all your code to be indendent on the SM) we can create two (or more) containers.
Firstly the Default one
public class DefaultProfileRegistry : Registry
{
public DefaultProfileRegistry()
{
// whatever calls needed to initialize this registry
SetScans(this); // scan
SetSetterInjection(this); // DI
Profile("DefaultProfile", SetDefaults); // even some common defaults
}
Now let's create a different Registry
public class SpecialProfileRegistry : Registry
{
public SpecialProfileRegistry()
{
DefaultProfileRegistry.SetScans(this); // use part from default
...
Profile("Special", DefaultProfileRegistry.SetDefaults); // common defaults
}
Well, what we have: Two Registry
. One is the default, the second is special could profit from it and adjust a part, or be totally different...
3) Register them in the IFactoryProvider
implementor e.g. StructureMapFactoryProvider
(SMFP):
public partial class StructureMapFactoryProvider : IFactoryProvider
{
private static readonly IContainer Special;
static StructureMapFactoryProvider()
{
// 1) the default registry container
ObjectFactory.Initialize(x =>
{
x.UseDefaultStructureMapConfigFile = false;
// Defaults
x.IncludeRegistry<DefaultProfileRegistry>();
});
ObjectFactory.Container.SetDefaultsToProfile("DefaultProfile");
// 2) and now register the other(s)
Special = new StructureMap.Container(new SpecialProfileRegistry());
Special.SetDefaultsToProfile("Special");
}
Well, now, when our SMFP is firstly touched, all containers are instantiated...
4) And finally inside the IFactoryProvider.GetInstance()
, we are be able to decide what to use
object IFactoryProvider.GetInstance(Type type)
{
var useSpecial = ... // get the information to decide
if (useDefault)
{
return Special.GetInstance(type);
}
else
{
return ObjectFactory.GetInstance(type);
}
5) The useSpecial
must be available somehow in this place. And must not be relied on the IFactoryProvider.GetInstance()
. If this value is a const during the complete request processing, the correct IContainer
will serve correct objects.
6) Each of these IContainer
could have different Conventions, different LiefCycle settings... Even in cases, that there are thousands of PluginType
s registered, this solution provides very good performance and is multithread safe (e.g. no Profile switching)
Upvotes: 1
Reputation: 1714
We could also change our factories to create objects explicitly, but that doesn't really work when we need to get an instance generically (
container.GetInstance<IMyType>()
).
This is actually doable supposing you have a limited number of places where you need it:
For<IMyType>().Use(s => new MyType());
These could of course also be symptoms of an underlying design problem, for instance the need to define a tenant specific context, so please spend some time carefully reconsidering your design.
Upvotes: 0