Interception: Can I pass parameter to ICallHandler?

Jul 19, 2011 at 7:30 AM

Hello.

I cannot find the way I could pass parameter from Resolve() calling code to ICallHandler implementation. Here is example:

    IMethodReturn ICallHandler.Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
    {

        Console.WriteLine(
            string.Format(
                "Begin {0} with param {1}",
                input.MethodBase.Name, 
                message // parameter I need to be passed from Resolve<>() calling code
            )
        );

        var result = getNext.Invoke()(input, getNext);

        Console.WriteLine("End " + input.MethodBase.Name);
        return result;
    }

Can I do this at all and if not, then what alternatives I have to reach this goal?

Jul 19, 2011 at 10:27 AM

Hi,

Is it correct that you want to pass a value from Resolve and you need to catch this from your call handler? If that is the case, then I'm afraid this isn't possible because the call handler can be invoked only if the method is intercepted (through matching rules). In order for the method to be intercepted, you need to call the method directly and pass its parameters.

 

Noel Angelo Bolasoc
Global Technologies and Solutions
Avanade, Inc.
entlib.support@avanade.com

Jul 19, 2011 at 10:43 AM

I really do not see logic here. Why it's impossible to pass some extra data throught call handlers, if I can read argument values there? What the diference between arguments I pass into method to be intercepted and extra data I pass into Resolve()?

Also I saw one man wrote that we can create ICallHandler with custom constructor and somehow pass parameters from Resolve to it. Is it possible? If it is true, than could I configure ICallHandler lifetime to be created every time I call method to be intercepted? If yes, so that's it! That's a solution.

Jul 20, 2011 at 7:33 AM

Hi,

To answer some of the questions here are as follows;

1. Why it's impossible to pass some extra data throught call handlers, if I can read argument values there?

    - It is possible to pass data to the Call Handler thru its constructor. You just need to set up the correct parameter in your Call Handler constructor. Argument values from the intercepted method can also be pass in the Call handler (specifically accessible thru the IMethodInvocation type parameter of the Invoke method of your Call Handler), this will happen once method is intercepted.

2. What the diference between arguments I pass into method to be intercepted and extra data I pass into Resolve()?

     - I'm not sure if we're on the same page on this, if what you mean is the container.Resolve() then the difference is that the Resolve is the actual object creation that was registered in the container. So it is more like the constructor call to the concrete class you have registered. While this is different from calling a method configured with interception.

 

We're really not sure about the exact scenario you're trying to achieve here. Can you kindly elaborate your scenario.

If what you mean is to pass parameter values to your Custom Call handler please see if this related thread would help - http://entlib.codeplex.com/discussions/262588.

Hope this helps.

Gino Terrado
Global Technologies and Solutions
Avanade, Inc.
Contact Us

Jul 20, 2011 at 7:52 AM
Edited Jul 20, 2011 at 7:55 AM

I trying to write interception for a bussiness layer of my application. Bussiness layer have web methods. When I call web method, some preparation needed to be done, before I can call real logic. This preparations are same for all bussiness layes web methods. So I decided to try to separate it as AOP aspect with Unity Interception. To perform preparation, I need WebService.Session property value. So this mean I need to pass it to interceptor (ICallHandler implementation) every time I resolve or I call method of resolved object. Of course I can pass it as parameter of method to be intercepted and use it only in ICallHandler.Invoke, but I do not want to change implementation of my bussiness logic methods. Also I have type based matching rule and I do not use Attributes to check methods I want to intercept.

I want to say, that parameter I need to pass is dynamic, not static like in case of http://entlib.codeplex.com/discussions/262588 . So I cannot simply define it in attribute or configuration.

Jul 20, 2011 at 11:05 AM

Then you can do this programmatically, instead of via configuration. Something like

 IUnityContainer container = new UnityContainer();
            container.AddNewExtension<Interception>();
            container.RegisterType<IDummyService, DummyService>
                (new Interceptor<InterfaceInterceptor>(), 
                new InterceptionBehavior<PolicyInjectionBehavior>());

            container.Configure<Interception>()                
                .AddPolicy("MyPolicy")
                .AddMatchingRule(
                    new MemberNameMatchingRule("SayHello"))
                    .AddCallHandler(new MyCustomHandler("MyPropertyValue"));

And in your Custom CallHandler...

 public class MyCustomHandler : ICallHandler
    {
        public string MyProperty { get; set; }

        public MyCustomHandler(string myProperty)
        {
            MyProperty = myProperty;
        }

        #region ICallHandler Members

        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
        {
            input.Arguments[0] = "Hello " + input.Arguments[0];
            IMethodReturn result = getNext()(input, getNext);
            return result;
        }

....................................................

Hope this helps.

Gino Terrado
Global Technologies and Solutions
Avanade, Inc.
Contact Us


Jul 20, 2011 at 11:22 AM
Edited Jul 20, 2011 at 12:01 PM

My web method look like this:

[WebMethod()]
public void PerformSomeLogic()
{
    var logicManager = Interception.Container.Resolve<IBussinessLogic>(); // where Interception.Container - preconfigured IUnityContainer
    logicManager.PerformSomeLogic();
}

I'd like to configure IUnityContainer once. But parameter I need to pass changes every time I call PerformSomeLogic() web method. As I said, parameter is dynamic (changes from call to call) and not known at ICallHandler (MyCustomHandler as in example) instance creation time. And in my case I need to pass WebService instance. As I tested, new WebService instance creates at any call to WebMethod.

Jul 21, 2011 at 1:23 PM

If this is the case my personal take is to make it as a method parameter. I don't fully understand your specific scenario though given that as mentioned this parameter value changes every call, I might prefer to have it as a method parameter.

Gino Terrado
Global Technologies and Solutions
Avanade, Inc.
Contact Us

Jul 21, 2011 at 1:34 PM
Edited Jul 21, 2011 at 1:50 PM

But I will not use new parameter in method realization. Also if I already wrote 100 IBussinessLogic methods, is this mean that now I have to change every single to use it with interception (with my ICallHandler implemetation)? In this case interception will not be as transparent as it should be.

And if this is a final answer, then why Unity Interception have no possibility to pass parameter like in this case? I think it is a usual scenario, when aspect realization depends on some parameter, that isn't known at container configuration time.

Jul 21, 2011 at 1:59 PM

Unfortunately, if this is your requirement then you need to. Just note that its not the interception method made you do this but your requirement is. As I mentioned I don't fully understand your scenario that's why it is really hard for us to suggest. Up from the initial problem you have mentioned from the first post it seems that it become different now.

If I were to answer the first post again regarding the "parameter I need to be passed from Resolve<>() calling code", I'd rather find ways to manipulate the input.Arguments and put my required parameter values I need to pass back to the method intercepted. But this became very confusing when you mentioned that the parameter is "not known at ICallHandler".

We're really lost here.

May I suggest, how about if we start it all over again and begin with more simple sample of your problem trying to solve.  :o)

Gino Terrado
Global Technologies and Solutions
Avanade, Inc.
Contact Us

 

Jul 21, 2011 at 2:36 PM
Edited Jul 21, 2011 at 2:39 PM

Really sorry that I confused you. So let's start from beginning. Here is code sample:

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.InterceptionExtension;

namespace Example
{

    public static class Utility
    {

        private static IUnityContainer _container;
        public static IUnityContainer Container
        {
            get { return Utility._container; }
        }

        static Utility()
        {
            // configuring Container here
            IUnityContainer container = new UnityContainer();
            // ...
        }


        public static void PrepareRequest(System.Web.SessionState.HttpSessionState session)
        {
            // perform some preparation actions
            // ...
        }

    }

    public interface IBussinessLogic : IBussinessLogic
    {
        void PerformSomeLogic();
    }

    public class BussinessLogic : IBussinessLogic
    {
        public BussinessLogic()
        {
            // perform some logic
            // ...
        }
    }

    internal class WebMethodCallHandler : ICallHandler
    {

        public int Order { get; set; }

        public LogCallHandler()
            : this(0)
        {
        }

        public LogCallHandler(int order)
        {
            this.Order = order;
        }

        IMethodReturn ICallHandler.Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
        {
            IMethodReturn result;
            try
            {
                Utility.PrepareRequest(session); // using of passed parameter
                result = getNext.Invoke()(input, getNext);
            }
            catch (Exception ex)
            {
                // handle exception
            }
            return result;
        }

    }

    public class MyService : System.Web.Services.WebService
    {

        // old way realization
        [System.Web.Services.WebMethod(EnableSession = true)]
        public void Method1()
        {
            try
            {
                Utility.PrepareRequest(this.Session);
                var logic = new BussinessLogic();
                logic.PerformSomeLogic();
            }
            catch (Exception ex)
            {
                // handle exception
            }
        }

        // new way realization
        [System.Web.Services.WebMethod(EnableSession = true)]
        public void Method2()
        {
            // somehow I need to pass this.Session value to ICallHandler implementation
            var logic = Utility.Container.Resolve<IBussinessLogic>();
            logic.PerformSomeLogic();
        }

    }

}

As you can see, session argument in ICallHandler.Invoke realization is not known. That's a problem.

Jul 23, 2011 at 2:44 PM

Now, I do agree it is tricky :o). The fact that the "session" is dynamic I believe you can't afford to only allow UnityContainer configuration once unless the "session" is an argument parameter of your BusinessLogic method. There may be some better ways to do it but as of the moment I may still need more researching to do.

Gino Terrado
Global Technologies and Solutions
Avanade, Inc.
Contact Us

 

 

Jul 23, 2011 at 3:16 PM

Ok. I'll wait.

I thought this situation is usual and must be a way to pass that kind of parameters. Otherwise I'll be very disappointed in Unity Interception and I will have to search other more elegant AOP approach =((

Aug 2, 2011 at 12:54 PM

I wonder how your researching going on. Is there something new discovered?

Aug 9, 2011 at 8:05 AM

Hi Robot1que,

Sorry for the late response. We have reached out to Microsoft regarding this and here is their reply:

"It’s not clear to me how he expects his scenario to work, and furthermore I’m not sure his custom call handler is actually a cross-cutting concerned: if this Utility.PrepareRequest method call is required for the business logic to work, then it isn’t really cross-cutting and is instead part of the contract for using the “business logic”. 

If I understand correctly he wants to supply the parameter to the Resolve call and have it make it to the call handler. While you can provide overrides to the Resolve call that can apply to objects deep down in the build stack, these overrides do not make it to the creation of the call handlers themselves (for which a new Resolve call is made). This might be what the customer is expecting, but I cannot really tell from his posts.

If he’s configuring policy injection with rules (rather than attributes), he could create a child container and configure it with the rule specific for that call, which would have the call handler pre-configured with the session, or do something to provide some transient policies (we’ve done that with the TransientPolicyBuildUpExtension in EntLib v5, used by the PolicyInjector class to target interception of a user-supplied object) and provide different policies for setting the call handlers on each Resolve call. If they’re using attributes then I guess they’d need some kind of thread-static storage to keep the “parameters” and be diligent about cleaning up this storage. I doubt other AOP approaches would be more elegant than this, particularly the ones that rely on code weaving."

I hope this somehow shed some light.

 

Noel Angelo Bolasoc
Global Technologies and Solutions
Avanade, Inc.
Contact Us

Aug 9, 2011 at 8:38 AM

Thank you for help.