Search code examples
c#simple-injectorinterception

Calling functions on intercepted method invocations in Simple Injector


What I am trying to achieve is to intercept the injection of a class, and call a specific method on the class to alter it's behaviour.

I have implemented the interceptor class that is given on the SimpleInjector website, and this is working, so I am able to get some functionality running when the class is intercepted.

My container is registering it as such:

container.InterceptWith<MyInterceptor>(type => type == typeof(IMyClass));

The class I am intercepting on looks as such:

public class MyClass : IMyClass
{
    private IAnotherClass m_class;
    public MyClass(IAnotherClass _class)
    {
         m_class = _class;
    }

    public void MethodToCall()
    {
         //changes properties on class
    }
}

My interceptor class looks as such:

public class MyInterceptor : IInterceptor
{
    private readonly ILogger logger;

    public MyInterceptor(ILogger logger)
    {
        this.logger = logger;
    }

    public void Intercept(IInvocation invocation)
    {
        var watch = Stopwatch.StartNew();

        // Calls the decorated instance.
        invocation.Proceed();

        var decoratedType = invocation.InvocationTarget.GetType();

        logger.Trace(string.Format("{0} executed in {1} ms.",
            decoratedType.Name, watch.ElapsedTicks));
    }
}

What I am trying to achieve is to call a method on the intercepted IMyClass. So in the interceptor, call MyClass.MethodToCall()

I have tried to do something like this in the Intercept() method:

var classIntercepted = invocation.ReturnValue;
MethodInfo method = invocation.InvocationTarget.GetType().GetMethod("MethodToCall");
object magicValue = method.Invoke(classIntercepted, null);

But, the invocation.ReturnValue is not returning the MyClass instance, but rather the IAnotherClass instance


Solution

  • Why don't you use a decorator instead of using interception? This is often much easier, more maintainable and faster.

    Here's an example:

    public class PropSetMyClassDecorator : IMyClass
    {
        private MyClass decoratee;
        public PropSetMyClassDecorator(MyClass decoratee) {
            this.decoratee = decoratee;
        }
    
        public void MethodToCall() {
            this.decoratee.SetConnectionString();
            this.decoratee.MethodToCall();
        }
    }
    

    You can register this decorator as follows:

    container.Register<IMyClass, PropSetMyClassDecorator>();
    

    Do note that instead of registering MyClass, we only register the decorator. Since the decorator directly depends on MyClass (not on the interface) MyClass will be automatically resolved by Simple Injector.

    Yet another option is to register an initializer as follows:

    container.RegisterInitializer<MyClass>(instance => {
        instance.SetConnectionString();
    });
    

    The initializer delegate will be called every time after a MyClass instance is constructed. The behavior is a bit different in this case, since the method isn't called every time, but only during construction. Usually however, this should be sufficient, since you should normally not change a service during runtime, since you are complicating things.