ipjohnson / Grace

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

Interception #184

Closed mcdis closed 5 years ago

mcdis commented 5 years ago

Hi Ian, thank you for your job! I need to autoregister types as singletone that implement some interface IFoo but I don't know exact types. for example if the class to activate depends on CustomFoo : IFoo than CustomFoo have to be resolved as singletone

ipjohnson commented 5 years ago

Hi @mcdis

I’ll see if I can find a test that shows something like this. Ultimately what I think you want to do is use the ‘UsingLifestyle’ method.

It takes a ‘Func<Type,ICompiledLifestyle>’ that will let you pick lifestyle based on the type being exported. Here you can put your logic to decide what should or shouldn’t be a singleton

mcdis commented 5 years ago

Can you show example of using UsingLifestyle?

ipjohnson commented 5 years ago

@mcdis I added a test showing how to use UsingLifestyle but I also remembered you could also use an IActivationStrategyInspector that would act on all exports.

public class StrategyInspector : IActivationStrategyInspector
{
      public void Inspect<T>(T strategy) where T : class, IActivationStrategy
      {
           if (strategy is ICompiledExportStrategy exportStrategy)
           {
                if (exportStrategy.ActivationType.GetTypeInfo().ImplementedInterfaces.Contains(typeof(IFoo)))
                {
                    exportStrategy.Lifestyle = new SingletonLifestyle();
                }
           }
       }
}
mcdis commented 5 years ago

Thank you! Can I control lifetime strategy for not exported types? example:

interface IFoo{}
class AFoo:IFoo {}
class BFoo:IFoo {}
class ConsumerCache
{
    public ConsumerCache(AFoo _a)
    {
        A = _a;
    }
    public AFoo A {get;}
}
class Consumer
{
  public Consumer(AFoo _a,BFoo _b,ConsumerCache _cache)
  {
     _cache.A == _a; // true, singletone
  }
}

var scope = new DependencyInjectionContainer();
scope.Locate<Consumer>();

So, I've got module like system and need to resolve some processors but I want to skip export phase because I know that every resolvable type which implement IFoo must to be singletone. Looks like I've to add some custom strategy...

In my case IFoo implementators can locate in different assemblies and from case to case I can use different set and I don't want to have export registration phase.

PS: StrategyInspector works only for explicit exported types.

ipjohnson commented 5 years ago

I’ll take a look later today as the inspectors should be run against all exports even non explicitly exported types.

ipjohnson commented 5 years ago

@mcdis I've added a new unit test here to show that interceptors are run against non exported types.

Can you provide an example app where it's not working?

mcdis commented 5 years ago
   interface IFoo { }
   class FooInspector : IActivationStrategyInspector
   {
      public void Inspect<T>(T _strategy) where T : class, IActivationStrategy
      {
         if (_strategy is ICompiledExportStrategy exportStrategy)
         {
            if (exportStrategy.ActivationType.GetTypeInfo().ImplementedInterfaces.Contains(typeof(IFoo)))
               exportStrategy.Lifestyle = new SingletonLifestyle();
         }
      }
   }
   class AFoo : IFoo { }
   class Test
   {
      public Test(AFoo _foo) => A = _foo;
      public AFoo A { get; }
   }
   class Program
   {
      static void Main()
      {
         var scope = new DependencyInjectionContainer();
         var child = scope.CreateChildScope(_ => _.AddInspector(new FooInspector()));
         var test = child.Locate<Test>();
         var a = child.Locate<AFoo>();
         var equals = test.A == a; // Fail 
      }
   }
mcdis commented 5 years ago

Works only with inspector in root scope. Doesn't work when use nested scopes

ipjohnson commented 5 years ago

@mcdis the reason for that is that missing strategies are registered in the root container not the child container where you’ve installed an inspector. While it doesn’t work the way you’d like it to it’s working correctly.

mcdis commented 5 years ago

Can I intercept missing types and do registration on the child scope level?

ipjohnson commented 5 years ago

@mcdis by default there is just one ConcreteExportStrategyProvider in the root container. You could add one in the child scope like so

scope.CreateChildScope(_ => 
{
   _.AddInspector(new FooInspector());
   _.AddMissingExportStrategyProvider(new ConcreteExportStrategyProvider()));
}
mcdis commented 5 years ago

@ipjohnson holy interface, that's works! AddMissingExportStrategyProvider + Inspector, yeah!