Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Castle Dynamic Proxy of Interface and not Derived Class

namespace DynamicInterception
{
    public class Calculator
    {
        public virtual int Div(int a, int b)
        {
            try
            {
                return a / b;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message.ToString());
                return 0;
            }
        }
    }

    [Serializable]
    public abstract class Interceptor : IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            ExecuteBefore(invocation);
            invocation.Proceed();
            ExecuteAfter(invocation);
        }
        protected abstract void ExecuteAfter(IInvocation invocation);
        protected abstract void ExecuteBefore(IInvocation invocation);
    }

    public class CalculatorInterceptor : Interceptor
    {
        protected override void ExecuteBefore(Castle.DynamicProxy.IInvocation invocation)
        {
            Console.WriteLine("Start: {0}", invocation.Method.Name);
        }

        protected override void ExecuteAfter(Castle.DynamicProxy.IInvocation invocation)
        {
            Console.WriteLine("End: {0}", invocation.Method.Name);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            ProxyGenerator generator = new ProxyGenerator();
            Calculator c = generator.CreateClassProxy<Calculator>(new CalculatorInterceptor());
            var r = c.Div(11, 0);
            Console.ReadKey();
        }
    }
}

Is it possible to replace public virtual int Div(int a,int b) with interface

interface ICalculator
{
    int Div(int a, int b);
}

How then should look like proxy declaration?

ProxyGenerator generator = new ProxyGenerator();
Calculator c = generator.CreateClassProxy<Calculator>(new CalculatorInterceptor());
like image 504
A191919 Avatar asked Aug 18 '16 08:08

A191919


1 Answers

If you want to add an interface to the Calculator and to execute those 2 lines it will work the same:

public interface ICalculator
{
    int Div(int a, int b);
}

public class Calculator : ICalculator
{

    public int Div(int a, int b)
    {
        try
        {
            return a / b;
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message.ToString());
            return 0;
        }
    }
}

ProxyGenerator generator = new ProxyGenerator();
Calculator c = generator.CreateClassProxy<Calculator>(new CalculatorInterceptor());

But you didn't really do anything by that - you are still creating the proxy for a concrete derived type. I assume you want something like "CreateClassProxy<ICalculator>". That won't work because the CreateClassProxy has a generic constraint on where TClass : class.

What you do have is a variety of CreateInterfaceProxt.. Methods which you can try. But still a naive execution like the following won't work:

ICalculator c = generator.CreateInterfaceProxyWithoutTarget<ICalculator>(new CalculatorInterceptor());
c.Div(1, 2);

It will execute, call the interceptor and will fail when running the invocation.Proceed(); with the error:

System.NotImplementedException This is a DynamicProxy2 error: The interceptor attempted to 'Proceed' for method 'Int32 Div(Int32, Int32)' which has no target. When calling method without target there is no implementation to 'proceed' to and it is the responsibility of the interceptor to mimic the implementation (set return value, out arguments etc)

So as the good indicative (seriously) errors of Castle specify - you must somehow have an implementation for it - or by indicating it yourself in the interceptor - of by having a Component registered for that interface.

Instead you can do like this: (Check comments in code)

ProxyGenerator generator = new ProxyGenerator();

ICalculator calculator = new Calculator();
var proxyCalculator = generator.CreateInterfaceProxyWithTarget(typeof(ICalculator),calculator, new CalculatorInterceptor());

calculator.Div(1, 2); // Will execute but will not be intercepted
((ICalculator)proxyCalculator).Div(11, 0); //Will execute and will be intercepted

But after saying all I said above, if the purpose behind all of this is to have an interceptor intercept your method then just just the "good-old" registering to the container:

WindsorContainer container = new WindsorContainer();
container.Register(
    Component.For<CalculatorInterceptor>(),
    Component.For<ICalculator>()
             .ImplementedBy<Calculator>()
             .Interceptors<CalculatorInterceptor>());

var calculator = container.Resolve<ICalculator>();
calculator.Div(1, 0);

// Output:
// Start: Div
// Attempted to divide by zero
// End: Div
like image 191
Gilad Green Avatar answered Oct 14 '22 15:10

Gilad Green