Share via


Creating a New Handler

Retired Content

This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.

The latest Unity Application Block information can be found at the Unity Application Block site.

The Unity Application Block defines an interface named ICallHandler, which all classes that implement handlers for a pipeline must implement. This interface declares a single method named Invoke that takes as parameters an instance of a class that implements the IMethodInvocation interface and a reference to an instance of the GetNextHandlerDelegate class that references the next handler in the pipeline (the handler to which this handler should pass control when it completes its own pre-processing stage). The IMethodInvocation implementation instance contains information about the current method invocation or property access, including the parameters to pass to the method or property.

The Invoke method returns an instance of a class that implements the IMethodReturn interface. This class instance contains any return value from the method or property accessor and any other information to return to the client. Usually, this is a concrete instance of the ReturnMessage class.

In addition, the ICallHandler interface defines two delegates. The first, named InvokeHandlerDelegate, is called by the previous handler to execute this handler—and therefore has the same signature as the Invoke method. The second, named GetNextHandlerDelegate, determines which is the next handler in the chain to invoke. The following extract shows the complete ICallHandler interface.

public interface ICallHandler
{
  IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext);
}

public delegate IMethodReturn InvokeHandlerDelegate(IMethodInvocation input,
                                                GetNextHandlerDelegate getNext);

public delegate InvokeHandlerDelegate GetNextHandlerDelegate();
'Usage
Public Interface ICallHandler
  Function Invoke(ByVal input As IMethodInvocation, _
     ByVal getNext As GetNextHandlerDelegate) As IMethodReturn
End Interface

Public Delegate Function InvokeHandlerDelegate( _
    ByVal input As IMethodInvocation, ByVal getNext As GetNextHandlerDelegate) _
    As IMethodReturn

Public Delegate Function GetNextHandlerDelegate() As InvokeHandlerDelegate

The following extract shows the basic outline of a custom handler class, indicating where you can add code to perform both the pre-processing and post-processing tasks you require.

[ConfigurationElementType(typeof(CustomCallHandlerData))]
public class ExampleHandler : ICallHandler
{
  public IMethodReturn Invoke(IMethodInvocation input,
                               GetNextHandlerDelegate getNext)
  {
    // Declare any variables required for values used in this method here.
    ...
    ...

    // Perform any pre-processing tasks required in the custom handler here.
    // This code executes before control passes to the next handler.
    ...
    ...

    // Use the following line of code in any handler to invoke the next 
    // handler that the application block should execute. This code gets
    // the current return message that you must pass back to the caller:
    IMethodReturn msg = getNext()(input, getNext);  

    // Perform any post-processing tasks required in the custom handler here.
    // This code executes after the invocation of the target object method or
    // property accessor, and before control passes back to the previous
    // handler as the Invoke call stack unwinds. You can modify the return 
    // message if required.
    ...
    ...

    // Return the message to the calling code, which may be the previous 
    // handler or, if this is the first handler in the chain, the client.   
    return msg;
  }
}
'Usage
<ConfigurationElementType(GetType(CustomCallHandlerData))> _
Public Class ExampleHandler : Implements ICallHandler

  Public Function Invoke(ByVal input As IMethodInvocation, _
                     ByVal getNext As GetNextHandlerDelegate) As IMethodReturn _
         Implements ICallHandler.Invoke

    ' Declare any variables required for values used in this method here.
    ...
    ...

    ' Perform any pre-processing tasks required in the custom handler here.
    ' This code executes before control passes to the next handler.
    ...
    ...

    ' Use the following line of code in any handler to invoke the next 
    ' handler that the application block should execute. This code gets
    ' the current return message that you must pass back to the caller:
    Dim msg As IMethodReturn = getNext()(input, getNext)

    ' Perform any post-processing tasks required in the custom handler here.
    ' This code executes after the invocation of the target object method or
    ' property accessor, and before control passes back to the previous
    ' handler as the Invoke call stack unwinds. You can modify the return 
    ' message if required.
    ...
    ...

    ' Return the message to the calling code, which may be the previous 
    ' handler or, if this is the first handler in the chain, the client.   
    Return msg
  End Function
End Class

The Invoke method receives a reference to an instance of a concrete implementation of the IMethodInvocation class. Your handler can access the properties of this instance to get information about the current method or property accessor call. For example, you can access the target object instance (Target), the name of the target method or property (MethodBase.Name), a count of the number of inputs (Inputs.Count), and a collection of all the parameter values (Arguments).

Note

Your code can change the values of the properties of the IMethodInvocation instance. However, be aware that changing the values of some properties may cause unexpected behavior. You should avoid changing any properties that affect the name, type, or signature of the target member.

The application block reuses handler instances in different pipelines to minimize the number of objects it must create. Therefore, handlers should not store any per-call state in member variables. In multi-threaded applications, the action of multiple calls passing through the same handler instance is likely to corrupt the internal state.

Controlling Handler Pipeline Execution and Returning Exceptions

Your handler may need to be able to short-circuit the pipeline and abort processing so that the following handlers do not execute and the application block does not invoke the target method or property accessor. In this case, your code should simply avoid calling the next handler and generate a suitable return message. In other words, you omit the code line shown in the previous example that calls the getNext method. This causes all the previous handlers to execute their post-processing tasks as the Invoke stack unwinds.

If your handler has to abort processing completely without allowing previous handlers to execute, you can create and raise a suitable exception type—depending on the tasks that your handler carries out. However, in most cases, you should avoid this approach and, instead, add to the message any exceptions you want to raise to the client so that previous handlers (which may perform logging or process exceptions) can execute their post-processing tasks.

To add an exception to the message, you create a new instance of the ReturnMessage class using the constructor that accepts a System.Exception instance or a subclass of System.Exception. For example, this code shows how you can add an exception to the return message when the current method request for some business-related activity occurs on a Saturday or Sunday.

public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
  GregorianCalendar cal = new GregorianCalendar();
  DayOfWeek weekDay = cal.GetDayOfWeek(DateTime.Now);
  if ((weekDay == DayOfWeek.Saturday) || (weekDay == DayOfWeek.Sunday))  
  {
    // Create the exception to return and the return message.
    Exception ex = new Exception("Available on weekdays only");
    IMethodReturn msg = input.CreateExceptionMethodReturn(ex);
    return msg;  
  }
  else
  {
    // Do nothing except invoke the next handler. 
    return getNext()(input, getNext);  
  } 
}
'Usage
Public Function Invoke(ByVal input As IMethodInvocation, _
                  ByVal getNext As GetNextHandlerDelegate) As IMethodReturn _
       Implements ICallHandler.Invoke

  Dim cal As New GregorianCalendar()
  Dim weekDay As DayOfWeek = cal.GetDayOfWeek(DateTime.Now)
  If (weekDay = DayOfWeek.Saturday) Or (weekDay = DayOfWeek.Sunday) Then

    ' Create the exception to return and the return message.
    Dim ex As New Exception("Available on weekdays only")
    Dim msg As IMethodReturn = input.CreateExceptionMethodReturn(ex)
    Return msg  

  Else
    ' Do nothing except invoke the next handler. 
    return getNext()(input, getNext)  
  End If 
End Function