dependency-injectionautofaccastle-dynamicproxy

How to dynamically instantiate a proxy class?


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.


Solution

  • 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.