1

I have a requirement were I need some instances of my domain to remain the same during a scope of my choosing. These instances are an exception and the norm should be that all instances are new each time they are requested by the domain. (The way transient life time should work)

Problem is I cant find a DI Container that works this way.

I have tested Castle Windsor and StructureMap.

In StructureMap you do

using(var nested = container.GetNestedContainer())
{
   var whatEver = nested.GetInstance<IWhatEver>();
}

The default will now be that the instances live during the entire life of the nested container. If you want the behavior I want you need to register all types that should be truly transient like

config
   .For<IMyTransientType>()
   .LifecycleIs(new UniquePerRequestLifecycle())
   .Use<MyTransientType>(); 

This will not work, I want the default behavior to be the other way around. So do you guys know a IoC that work this way, or that can be configured so that default behavior is like this.

edit: For the curious you can check out this working branch here using Structure map, https://github.com/AndersMalmgren/FreePIE/blob/MoveToStructureMap/FreePIE.Core/Common/StructureMap/StructureMapScopedContext.cs. You can anywere in the domain create a scoped life time using the interface IScopedContext<TEntryPoint>. Problem is that it defualts to "scoped" lifetime instead of transient life time. To get real transient life time you need set it to always unique. I have solved it for unregister concrete types with this little hacky helper

https://github.com/AndersMalmgren/FreePIE/blob/MoveToStructureMap/FreePIE.Core/Common/StructureMap/ConcreteTypeConventionScanner.cs

From the DI bootstrapper I then do

var assebmlies = container
    .GetInstance<IPluginDataSource>()
    .ListAllPluginTypes()
    .GroupBy(t => t.Assembly)
    .Select(g => g.Key);

assebmlies.ForEach(a => container.Configure(config => config.Scan(scan =>
{
    scan.Assembly(a);
    scan.Convention<ConcreteTypeRegistrationConvention>();
})));
Anders
  • 17,306
  • 10
  • 76
  • 144
  • As a rule of thumb, Castle Windsor can be configured to do *anything* you want it to do. If nothing else, it enables you to write your own custom lifestyle. See section 10.2.3 of [my book](http://amzn.to/12p90MG) for details. – Mark Seemann Sep 16 '15 at 18:03
  • 3
    Please share the details of those "instances of [your] domain" that should be cached for the duration of your scope. Please update your question with code samples of such 'instance'. – Steven Sep 16 '15 at 19:03
  • Pretty complex domain. But the general idea is that we have life time scopes (Similar to a Web api request scope). During this scope I want the default to be always unique, not the other way around. – Anders Sep 17 '15 at 08:35
  • Update with more info – Anders Sep 17 '15 at 08:41
  • You want transient *within* the scope but what do you want *outside* of the scope: a singleton? – qujck Sep 17 '15 at 14:21
  • No, i want transient (Real transient aka always new reference) during a scope if nothing else is configured. If I want same reference during a scope I want to configure that explicit. The other way around from how Structuremap and Castle works – Anders Sep 18 '15 at 13:37

1 Answers1

2

Simple Injector offers support for a variety of scoped lifestyles without the need for child containers. By default Simple Injector will return Transient within any lifetime scope unless explicitly instructed to do otherwise. Here are 3 tests to demonstrate the functionality ...

Transient with no scope

[Fact]
public void GetInstance_NoSpecificLifestyleOutsideOfAnyScope_IsAlwaysANewInstance()
{
    var container = new Container();
    container.Register<IService, Service>();
    container.Verify();

    var a = container.GetInstance<IService>();
    var b = container.GetInstance<IService>();
    Assert.NotSame(a, b);
}

Non transient IService registered with an explicit lifestyle

[Fact]
public void GetInstance_SpecificLifestyleWithinAScope_IsTheSameInstance()
{
    var container = new Container();
    container.Options.DefaultScopedLifestyle = new LifetimeScopeLifestyle();
    container.Register<IService, Service>(Lifestyle.Scoped);
    container.Verify();

    using (container.BeginLifetimeScope())
    {
        var a = container.GetInstance<IService>();
        var b = container.GetInstance<IService>();
        Assert.Same(a, b);
    }
}

Transient IService resolved within a lifetime scope

[Fact]
public void GetInstance_NoSpecificLifestyleWithinAScope_IsAlwaysANewInstance()
{
    var container = new Container();
    container.Options.DefaultScopedLifestyle = new LifetimeScopeLifestyle();
    container.Register<IService, Service>();
    container.Verify();

    using (container.BeginLifetimeScope())
    {
        var a = container.GetInstance<IService>();
        var b = container.GetInstance<IService>();
        Assert.NotSame(a, b);
    }
}
Steven
  • 166,672
  • 24
  • 332
  • 435
qujck
  • 14,388
  • 4
  • 45
  • 74
  • Nice, will have a look into that library. It needs to support Func factories too. Thats not a problem? edit: Nice looks like you guys are fast too – Anders Sep 19 '15 at 14:06
  • Hmm, I ahve a special requirement, I need to register the scope creator as a specific interaface, so that anyone can dispose the scope. I did it like this with StructureMap, do you know how todo it with SimpleInjector? https://github.com/AndersMalmgren/FreePIE/blob/MoveToStructureMap/FreePIE.Core/Common/StructureMap/StructureMapScopedContext.cs#L20 – Anders Sep 21 '15 at 19:11
  • You could create an abstraction around [`container.BeginLifetimeScope()`](https://simpleinjector.readthedocs.org/en/latest/lifetimes.html#perlifetimescope) but what would you expect to happen to any currently resolved instances when their owning scope is disposed? Can you define the requirement, instead of the solution, as a new question either on SO or the [forum](https://github.com/simpleinjector/SimpleInjector/issues)? I'm sure there will be a compatible solution .... – qujck Sep 21 '15 at 22:19
  • That abstraction needs to be added to the scope to so no difference – Anders Sep 22 '15 at 07:24
  • Any child in the current scope should be able to end the scope, like this https://github.com/AndersMalmgren/FreePIE/blob/MoveToStructureMap/FreePIE.Core/ScriptEngine/Python/PythonScriptEngine.cs#L333. All this is working with StructureMap – Anders Sep 22 '15 at 07:25
  • 1
    I can understand the concept of *"Any child in the current scope should be able to end the scope"* but imagine for a moment you have an `IDisposable` object in that scope; any `IDisposable` objects should instantly be `Disposed` along with the scope and you have a bug that will be very hard to track down. IMO ending a scope **inside** the object graph is a dangerous thing to do. *"Just because you can, doesn't mean you should"* ;-) – qujck Sep 22 '15 at 08:07