ipjohnson / Grace

Grace is a feature rich dependency injection container library
MIT License
337 stars 33 forks source link

Question about lifestyles #98

Closed darkcamper closed 7 years ago

darkcamper commented 7 years ago

@ipjohnson

Hi!

I have some IDisposable implementations for which I only want a single instance created per named scope ( using SingletonPerNamedScope works ok for this). But I also need sometimes to inject a factory for these IDisposable that would create always new instances that are not tracked by the scope (and threfore not disposed when the scope is disposed).

Here a small sample of what I want to do:

 public class Disposable : IDisposable    {  ...  }
 public class DisposableFactory 
 {
        Func<Disposable> _fact;
        public DiposableFactory(Func<Disposable> fact) { _fact = fact }
        public Disposable Get() { return  _fact(); }
 }
container.Configure( x => {
      x.Export<Disposable>().Lifestyle.SingletonPerNamedScope(SCOPE_NAME);
});

var scope = container.BeginLifetimeScope( SCOPE_NAME );
var d1 = scope.Locate<Disposable>(); //This should create a new instance of Disposable
var d2 = scope.Locate<Disposable>(); //This should return the same instance as d1
var f = scope.Locate<DisposableFactory>(); 
var d3 = f.Get(); // d3 gets a new instance
var d4 = f.Get(); // d4 gets another new instance

scope.Dispose() //d1 (and d2 as it's same instance should be disposed, but d3 and d4 shouldn't)

I've thought (and tried) of using Func<Scoped> and Func<Owned> but they don't really behave the way I need.

Is there any combination of special types, lifestyles or other configuration options that would work in this case?

Thanks.

ipjohnson commented 7 years ago

@darkcamper , ultimately the problem comes down to you created one registration with a lifestyle but want to use the same registration in two different ways.

In one situation you want the container to create Dispoable as a sington per scope. In the other situation you want the container to create a transient with no disposal tracking.

Probably the easiest solution is to do a second export of the same type but use the AsKeyed and ExtenallyOwned (so instances aren't tracked) in lue of the lifestyle. Then you can do a Locate(withKey: "key");

Later today I maybe able to put together a demo of what I'm thinking but at the moment I'm on feeding and diaper duty.

ipjohnson commented 7 years ago

I've added two unit tests as examples of what I was thinking at the bottom here.

I've also opened issue #99 to support using Func<T>

darkcamper commented 7 years ago

I was pretty sure I would need another export for the factories but I didn't realize the key was in the keys (no pun intended). I've read the new unit tests you've made, and now it's all clear to me.

Thank you @ipjohnson !