Anders Ekdahl
Anders Ekdahl

Reputation: 22933

Force the creation of an object in StructureMap

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

Answers (2)

Radim K&#246;hler
Radim K&#246;hler

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 PluginTypes registered, this solution provides very good performance and is multithread safe (e.g. no Profile switching)

Upvotes: 1

Filippo Pensalfini
Filippo Pensalfini

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

Related Questions