ipjohnson / Grace

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

Intercept selected implementations #227

Closed cnshenj closed 5 years ago

cnshenj commented 5 years ago

Preparation:

public interface IFoo { int GetCount(); }
public class Foo1 : IFoo { public int GetCount() => 1; }
[Slow]
public class Foo2 : IFoo { public int GetCount() { /* Lengthy operation, then return */ } }

public interface IBar { void Greet(); }
[Slow]
public class Bar : IBar { public void Greet() { /* Length operation */ } }

public class ThrottleInterceptor : IInterceptor { /* Throttle logic */ }

container.Configure(c => c.ExportAs<Foo1, IFoo>());
// Depending on app requirements, Foo2 may be registered
// container.Configure(c => c.ExportAs<Foo2, IFoo>());
container.Configure(c => c.ExportAs<Bar, IBar>());

Expected:

private bool ShouldIntercept(InjectionTargetInfo target) =>
    target.LocateType.GetCustomAttributes(typeof(SlowAttribute)).Length > 0;

container.Configure(c => c.Intercept(ShouldIntercept, typeof(ThrottleInterceptor)));

Requirement:

  1. Register once, both Foo2 and Bar will be intercepted, because they are [Slow].
  2. Foo1 is not intercepted, although it implements the same interface as Foo2.
cnshenj commented 5 years ago

Current ExportDecorator solution:

  1. Uses generics, so I can't batch register many types in one call.
  2. Only has access to service type information, but not other registration information. The workaround is to add logic to the interceptor to check IInvocation parameter. But then the type is already intercepted, which it doesn't need.
ipjohnson commented 5 years ago

Hi @cnshenj

Which interception library are you planning to use (since Grace doesn't have one built in)?

cnshenj commented 5 years ago

Castle DynamicProxy. But I am open to suggestions if a library can do what I want to do (with reasonable performance).

ipjohnson commented 5 years ago

I've added a simple C# extension that applies decorators to exported classes that have a specific attribute.

Let me know if that works for you

cnshenj commented 5 years ago

Thanks for the extension, I believe it will work. I just need to modify it to accept a Predicte<IActivationStrategy> so it can be used in other scenarios.

One question: you already checked the attribute once at the beginning of the foreach loop, why adding the same check again in .When.MeetsCondition()?

ipjohnson commented 5 years ago

The reason for the second check is because we’re intercepting the interface and there is a possibility for more than one type registered with the container.

Put it another way the decorator registration says I want to use this decorator for this interface when the implementation is type X.

cnshenj commented 5 years ago

Makes sense. And I can confirm it works.

I made two changes:

  1. As I mentioned earlier, using a Predicte<IActivationStrategy> so it can be used in more scenarios.
  2. Instead of strategy.ExportAs.First(), I use foreach (var serviceType in strategy.ExportAs) to intercept all service types in the strategy.
ipjohnson commented 5 years ago

@cnshenj both good modifications. I'll see about updating the implementation in the tests for anyone that's interested