Checking Parameters in an IMessageSink

Listing 11-10 shows the implementation of the CheckerSink. Calls from SyncProcessMessage() and AsyncProcessMessage() have been added to the private DoCheck() method, which iterates over the assigned attributes and forwards the business logic checks to CheckAttribute.DoCheck() for each parameter that is marked with this attribute.

Listing 11-10: The CheckerSink

start example
 using System; using System.Reflection; using System.Runtime.Remoting; using System.Runtime.Remoting.Activation; using System.Runtime.Remoting.Contexts; using System.Runtime.Remoting.Messaging; namespace ContextBound {    public class CheckerSink: IMessageSink    {       IMessageSink _nextSink;       String _mType;       public CheckerSink(IMessageSink nextSink, String mType)       {          _nextSink = nextSink;          _mType = mType;       }       public IMessage SyncProcessMessage(IMessage msg)       {          DoCheck(msg);          return _nextSink.SyncProcessMessage(msg);       }       public IMessageCtrl AsyncProcessMessage(IMessage msg,          IMessageSink replySink)       {          DoCheck(msg);          return _nextSink.AsyncProcessMessage(msg,replySink);       }       public IMessageSink NextSink       {          get          {             return _nextSink;          }       }       private void DoCheck(IMessage imsg)       {          // not interested in IConstructionCallMessages          if (imsg as IConstructionCallMessage != null) return;          // but only interested in IMethodMessages          IMethodMessage msg = imsg as IMethodMessage;          if (msg == null) return;          // Check for the Attribute          MemberInfo methodbase = msg.MethodBase;          object[] attrs = methodbase.GetCustomAttributes(false);          foreach (Attribute attr in attrs)          {             CheckAttribute check = attr as CheckAttribute;             // only interested in CheckAttributes             if (check == null) continue;             // if the method only has one parameter, place the check directly             // on it (needed for property set methods)             if (msg.ArgCount == 1)             {                check.DoCheck(msg.Args[0]);             }          }          // check the Attribute for each parameter of this method          ParameterInfo[] parms = msg.MethodBase.GetParameters();          for (int i = 0;i<parms.Length;i++)          {             attrs = parms[i].GetCustomAttributes(false);             foreach (Attribute attr in attrs)             {                CheckAttribute check = attr as CheckAttribute;                // only interested in CheckAttributes                if (check == null) continue;                // if the method only has one parameter, place the check directly                // on it (needed for property set methods)                check.DoCheck(msg.Args[i]);             }          }       }    } } 
end example

You can then change the sample client to demonstrate what happens when it performs an invalid operation, as shown in Listing 11-11.

Listing 11-11: This Client Does Not Honor the Business Logic Constraints

start example
 using System; using System.Runtime.Remoting.Contexts; namespace ContextBound {    public class TestClient    {       public static void Main(String[] args) {          Organization org = new Organization();          try          {             Console.WriteLine('Will set the name');             org.Name = 'Happy Hackers';             Console.WriteLine('Will donate');             org.Donate(99);             Console.WriteLine('Will donate more');             org.Donate(102);          }          catch (Exception e)          {             Console.WriteLine('Exception: {0}',e.Message);          }          Console.WriteLine('Finished, press <return> to quit.');          Console.ReadLine();       }    } } 
end example

When you start this application, you will get the output shown in Figure 11-2.

click to expand
Figure 11-2: The client's illegal operation is prohibited by the CheckerSink.

Great! You are now checking your business logic constraints by using attributes that are assigned at the metadata level instead of checks that are hidden in your source code.

One interesting consideration that I have not yet mentioned is the following: what would happen if the first Organization object instantiates another Organization object and calls the Donate() method on the secondary object? Will this call also go through the message sink? In fact, in the current configuration it won't. This example just protects your class library from "outside" clients but doesn't affect any calls inside this context. This is because the CheckableAttribute's IsContextOK() only requests a new context when it's called from outside a checked context.

To make all calls to Organization (no matter what their origin) go through the CheckerSink, you'd have to change CheckableAttribute to return false from IsContextOK():

 public override bool IsContextOK(Context ctx, IConstructionCallMessage ctor) {     return false; } 

This will request a new context for each and every instance of any class that is marked with [Checkable] and that inherits from ContextBoundObject.


Something you should never forget when using these techniques: you are dealing with remote objects! This also means that they are lifetime-managed by leases and will time out the same way as "conventional" remote objects do.You might therefore want to add a sponsor to it as shown in Chapter 6.

Advanced  .NET Remoting C# Edition
Advanced .NET Remoting (C# Edition)
ISBN: 1590590252
EAN: 2147483647
Year: 2002
Pages: 91
Authors: Ingo Rammer © 2008-2017.
If you may any questions please contact us: