A while back, Nick posted how you can use the Spring .NET Framework to have Design by Contract with AOP. I’ve been talking to him about you can do the same thing using a .NET remoting proxy to your object and intercept the calls to your objects. To do this, I needed three classes: An attribute to initialize the attaching at class level for wiring the AOP guts, a class that wires up the AOP guts and a sink that performs the interception. This is a modified version of Nick’s code:
using System;
using System.Reflection;
using System.Runtime.Remoting.Activation;
using System.Runtime.Remoting.Contexts;
using System.Runtime.Remoting.Messaging;
namespace Lozanotek
{
public interface IEvaluator
{
bool Evaluate(object arg);
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class PrologAttribute : Attribute
{
private int index;
public IEvaluator evaluator;
public PrologAttribute() { }
public PrologAttribute(int index, Type evalType, params object[] args)
{
this.Index = index;
Object obj = Activator.CreateInstance(evalType, args);
if (obj != null)
this.evaluator = obj as IEvaluator;
}
public IEvaluator Evaluator
{
get { return evaluator; }
set { evaluator = value; }
}
public int Index
{
get { return index; }
set { index = value; }
}
public bool Evaluate(object arg) { return evaluator.Evaluate(arg); }
}
public class StringLengthEvaluator : IEvaluator
{
private int min = 0;
private int max = 0;
public StringLengthEvaluator(int min, int max)
{
this.min = min;
this.max = max;
}
public bool Evaluate(object arg)
{
int len = arg.ToString().Length;
return (len <= max) && (len >= min);
}
}
[AttributeUsage(AttributeTargets.Class)]
public class AOPClassAttribute : Attribute, IContextAttribute
{
public void GetPropertiesForNewContext(IConstructionCallMessage msg)
{
msg.ContextProperties.Add(new AOPProperty());
}
public bool IsContextOK(Context ctx, IConstructionCallMessage msg) { return false; }
}
internal class AOPProperty : IContextProperty, IContributeServerContextSink
{
public AOPProperty() { }
public void Freeze(Context newContext) { }
public bool IsNewContextOK(Context newCtx) { return true; }
public string Name
{
get { return "AOPProperty"; }
}
public IMessageSink GetServerContextSink(IMessageSink nextSink)
{
return new AOPMessageSink(nextSink);
}
}
internal class AOPMessageSink : IMessageSink
{
private IMessageSink next;
public AOPMessageSink(IMessageSink next) { this.next = next; }
public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink) { return null; }
public IMessageSink NextSink
{
get { return next; }
}
public IMessage SyncProcessMessage(IMessage msg)
{
bool proceed = true;
if (msg is IMethodMessage)
{
proceed = Invoke(msg as IMethodMessage);
}
return (proceed) ? NextSink.SyncProcessMessage(msg) :
new ReturnMessage(null, null, 0, null, (IMethodCallMessage) msg);
}
private bool Invoke(IMethodMessage msg)
{
bool results = true;
MethodBase method = msg.MethodBase;
Type attrType = typeof(PrologAttribute);
PrologAttribute[] prologs = method.GetCustomAttributes(attrType, true) as PrologAttribute[];
if (prologs != null)
{
foreach (PrologAttribute pl in prologs)
{
if (pl != null)
{
IEvaluator eval = pl.Evaluator as IEvaluator;
if (eval != null)
{
object[] args = msg.Args;
results = eval.Evaluate(args[pl.Index]);
if (!results) { break; }
}
}
}
}
return results;
}
}
[AOPClass]
public class Person : ContextBoundObject
{
[Prolog(0, typeof(StringLengthEvaluator), 0, 5)]
public void SayHello(string name)
{
Console.WriteLine("Hello {0}", name);
}
}
class Program
{
[STAThread]
static void Main(string[] args)
{
Person p = new Person();
p.SayHello("Javier");
p.SayHello("Jav");
Console.ReadLine();
}
}
}
If you have any questions or comments, let me know.