0

I have used Castle.DynamicProxy to create an interceptor that implements IInterceptor. This interceptor does some work related with logging.

I have successfully injected this into multiple classes using the default Microsoft Dependency Injection and I also was able to do so using Autofac.

Microsoft Dependency Injection:

        public static void AddLoggedScoped<TService, TImplementation>(this IServiceCollection pServices)
            where TService : class
            where TImplementation : class, TService
        {
            pServices.TryAddScoped<IProxyGenerator, ProxyGenerator>();
            pServices.AddScoped<TImplementation>();
            pServices.TryAddTransient<LoggingInterceptor>();
            pServices.AddScoped(provider =>
            {
                var proxyGenerator = provider.GetRequiredService<IProxyGenerator>();
                var service = provider.GetRequiredService<TImplementation>();
                var interceptor = provider.GetRequiredService<LoggingInterceptor>();
                return proxyGenerator.CreateInterfaceProxyWithTarget<TService>(service, interceptor);
            });
        }

Autofac Dependency Injection:


 builder.RegisterType<DITest>( ).As<IDITest>()
               .EnableInterfaceInterceptors()
               .InterceptedBy(typeof(LoggingInterceptorAdapter<LoggingInterceptor>));

Despite this I would also like to inject it in classes dynamically instantiated (for instances, classes that are instantiated accordingly to a value - factory pattern). My factory instantiates different concretizations of an interface depending on a value provided by parameter. Something along these lines:

        public IApple Create(string color)
        {
            IApple fruit;

            switch (color)
            {
                case "green":
                    fruit = new GreenApple();
                    break;
                case "red":
                    fruit = new RedApple();
            }

            return fruit;
        }

The interface IFruit looks like these:

    public interface IFruit
    {
        void Cut();
        void Eat();
        string GetNutrionalInfo();
    }

What I am trying to achieve is a way to inject/add an interceptor to the concretization of RedApple() that would allow me to know when methods such as redApple.Cut() are called.

What is the best way to do so? I was under the impression that Autofac would allow this, but I have not been successful.

  • 1
    The question isn't clear - you say you've injected the class using MEDI and Autofac (which happens at runtime, like, not compile time) and now you want to... inject at runtime? You are already doing that. Are you looking for _service location_? – Travis Illig Oct 27 '22 at 01:39
  • I am sorry about the imprecision. I mean classes that are instantiated dynamically, for instances, using a factory pattern. Do you have some insights on this? – Diana Barros Oct 27 '22 at 09:48
  • 1
    It would be good to provide a code example - even if it's pseudocode - to show exactly what you're talking about. It's still not really clear what you're trying to get, but it's sounding like service location. – Travis Illig Oct 27 '22 at 14:06
  • I want all instances of a class instantiated at any given time to be intercepted. If anywhere in my code I do something like: var class = new MyClass(); class.DoSomething(); I want the DoSomething() method to be logged. – Diana Barros Oct 27 '22 at 14:44
  • If you are calling `new` then you're skipping all the interception mechanisms. There is no way to do that. Again, if it's more complicated than that, like if you have a factory, I'd recommend _updating the question_ because there may be other solutions. Show the factory getting injected somewhere, maybe show a simple version of the factory. But if you're just straight calling `new` that's not a DI thing, that's something like [code weaving](https://codingcanvas.com/code-weaving-using-fody/) or something more complex. – Travis Illig Oct 27 '22 at 18:16
  • I added some extra information about my factory to the post. My factory instantiates different concretizations of an interface accordingly to a given value. I would like to add an interceptor to this object that would be able to intercept calls to each of its methods. – Diana Barros Oct 28 '22 at 10:30

1 Answers1

1

What you will need to do is update your factory to use service location instead of directly constructing things. Basically, instead of using new, you'll need to use Autofac or Microsoft DI (assuming Autofac is configured as the backing container) to resolve the thing.

First, whenever you need your factory, make sure you are injecting it and not just calling new. Everything involved in this chain needs to go through Autofac.

public class UsesTheFactory
{
  private IFactory _factory;
  public UsesTheFactory(IFactory factory)
  {
    this._factory = factory;
  }
}

You will, of course, need to register the thing that uses the factory.

builder.RegisterType<UsesTheFactory>();

Next, inject the lifetime scope into the factory and use it for service location. This is how you get the proxy and all that into the created objects.

public class MyFactory : IFactory
{
  private readonly ILifetimeScope _scope;
  public MyFactory(ILifetimeScope scope)
  {
    this._scope = scope;
  }

  public IApple Create(string color)
  {
    IApple fruit;

    switch (color)
    {
      case "green":
        fruit = this._scope.Resolve<GreenApple>();
        break;
      case "red":
        fruit = this._scope.Resolve<RedApple>();
    }

    return fruit;
  }
}

You'll need to register the factory and the things that the factory needs to resolve.

builder.RegisterType<MyFactory>().As<IFactory>();
builder.RegisterType<RedApple>();
builder.RegisterType<GreenApple>();

Finally, whenever you need something that uses the factory, that thing needs to be resolved. In this example, you can't really ever just new UsesTheFactory() - you have to resolve it (or have it injected into something else).

var builder = new ContainerBuilder();
builder.RegisterType<UsesTheFactory>();
builder.RegisterType<MyFactory>().As<IFactory>();
builder.RegisterType<RedApple>();
builder.RegisterType<GreenApple>();
var container = builder.Build();

using var scope = container.BeginLifetimeScope();
var user = scope.Resolve<UsesTheFactory>();
user.DoSomethingThatCallsTheFactory();

The key principle is that if you need that proxy injected anywhere in the pipeline, you can't use new. Full stop. If you need that thing, it needs to flow through Autofac somehow.

Travis Illig
  • 23,195
  • 2
  • 62
  • 85