Click here to Skip to main content
Click here to Skip to main content

Intercepting method calls in C#, an approach to AOSD

By , 2 Oct 2004
 

Preword

AOP is a concept that is reasonably new to me, and I have only been reading about it in detail over the past week. I have read a number of articles here on CodeProject[1] and have realized that AOP can solve a recurring problem that I, and I'm sure many others face. I have a situation where I have a potentially huge number of method calls which all look very similar:

public void DoSometing()
{
  // Trace method call by name with any arguments (Debug Only)
  try
  {
    // Do some functionality
    // Trace call completion (Debug Only)
  }
  catch(Exception e)
  {
    // Trace Exception (Debug & Release)
    // Throw new exception or rethrow e
  }
}

If I could define AOP in a sentence, it would be: "To intercept method calls and do something meaningful either before it's executed or afterwards". The textbook uses for such an idea are pretty straightforward.

  • Logging
  • Error Handling
  • Security
  • Performance Monitoring
  • Transaction Management

The first two above pretty much cover my current requirement, the next two are also of great interest to me. It was this realization that has led me to think about a simple framework for dealing with this.

Introduction

The main concept behind this framework is to be able to intercept calls to an object's methods and properties, and optionally pre, or post-process that call.

In order to intercept these method calls, I have made use of ContextBoundObject - this is significant in that there is much discussion as to this class' suitability. My personal opinion, after running many tests, is that the performance hit is well worth the potential gain. However, I can tell you that some simple calls run many many times slower when an object simply inherits from ContextBoundObject. The other pitfall of ContextBoundObject is that you must inherit directly from it; with no multi-inheritance, this causes some head-scratching. I urge the reader to investigate this object before deciding whether it is suitable for your purpose.

The code in this article makes use of Reflection, Remoting and Diagnostics namespaces, and an understanding of them is assumed.

Interfaces

The main goal of this project is to write some simple classes which process method calls. I needed to start with some interfaces to define this behavior.

public interface IPreProcessor
{
    void Process(ref IMethodCallMessage msg);
}

public interface IPostProcessor
{
    void Process(IMethodCallMessage callMsg, ref IMethodReturnMessage retMsg);
}

These two interfaces accurately define the behavior that I am looking for. For clarity, the IMethodCallMessage and IMethodReturnMessage interfaces are both defined in the System.Runtime.Remoting.Messaging namespace, and represent the message sent to and from the method call. The intricacies of this system are beyond the scope of this article.

Attributes

With my processing interfaces defined, I need a way of indicating to a method that I wish to pre, or post process it, and the type of processor to use. The following two attributes match up with an interface above.

[AttributeUsage(AttributeTargets.Constructor | 
  AttributeTargets.Method | AttributeTargets.Property, 
  AllowMultiple=true)]
public class PreProcessAttribute : Attribute
{
    private IPreProcessor p;
    public PreProcessAttribute(Type preProcessorType)
    {
        this.p = Activator.CreateInstance(preProcessorType) as IPreProcessor;
        if(this.p == null)
            throw new ArgumentException(String.Format("The type '{0}' " + 
              "does not implement interface IPreProcessor", 
              preProcessorType.Name,"processorType"));
    }

    public IPreProcessor Processor
    {
        get{ return p; }
    }
}

[AttributeUsage(AttributeTargets.Method | 
  AttributeTargets.Property,AllowMultiple=true)]
public class PostProcessAttribute : Attribute
{
    private IPostProcessor p;
    public PostProcessAttribute(Type postProcessorType)
    {
        this.p = Activator.CreateInstance(postProcessorType) as IPostProcessor;
        if(this.p == null)
            throw new ArgumentException(String.Format("The type '{0}' " + 
              "does not implement interface IPostProcessor", 
              postProcessorType.Name,"processorType"));
    }

    public IPostProcessor Processor
    {
        get{ return p; }
    }
}

The last attribute is the one which will be used to indicate a class that we wish to intercept method calls on. It is special in that it derives from ContextAttribute as it will be used on a class which derives from ContextBoundObject.

[AttributeUsage(AttributeTargets.Class)]
public class InterceptAttribute : ContextAttribute
{
    
    public InterceptAttribute() : base("Intercept")
    {
    }

    public override void Freeze(Context newContext)
    {            
    }

    public override void 
           GetPropertiesForNewContext(IConstructionCallMessage ctorMsg)
    {
        ctorMsg.ContextProperties.Add( new InterceptProperty() );
    }

    public override bool IsContextOK(Context ctx, 
                         IConstructionCallMessage ctorMsg)
    {
        InterceptProperty p = ctx.GetProperty("Intercept") 
                                             as InterceptProperty;
        if(p == null)
            return false;
        return true;
    }

    public override bool IsNewContextOK(Context newCtx)
    {
        InterceptProperty p = newCtx.GetProperty("Intercept") 
                                             as InterceptProperty;
        if(p == null)
            return false;
        return true;
    }

}

Messaging

The code that brings all this together is contained in InterceptProperty, and InterceptSink. The latter participates in a messaging chain similar to remoting except it is across contexts rather than AppDomains.

The main code is in the InterceptSink which calls the following method before the method call:

private void PreProcess(ref IMethodCallMessage msg)
{
  PreProcessAttribute[] attrs 
    = (PreProcessAttribute[])
       msg.MethodBase.GetCustomAttributes(typeof(PreProcessAttribute), true);
  for(int i=0;i<attrs.Length;i++)
    attrs[i].Processor.Process(ref msg);
}

and this one after:

private void PostProcess(IMethodCallMessage callMsg, 
                                 ref IMethodReturnMessage rtnMsg)
{
  PostProcessAttribute[] attrs 
    = (PostProcessAttribute[])
       callMsg.MethodBase.GetCustomAttributes(typeof(PostProcessAttribute),true);
  for(int i=0;i<attrs.Length;i++)
    attrs[i].Processor.Process(callMsg,ref rtnMsg);
}

Implementation

In order to test this, I created a simple pre-processor to trace the name of the method to the standard Trace.

public class TracePreProcessor : IPreProcessor
{
 public TracePreProcessor()
 {}
 #region IPreProcessor Members
 public void Process(ref IMethodCallMessage msg)
 {
  this.TraceMethod(msg.MethodName);
 }
 #endregion
 [Conditional("DEBUG")]
 private void TraceMethod(string method)
 {
  Trace.WriteLine(String.Format("PreProcessing:{0}",method));
 }
}

(Note the Conditional attribute - useful if I only want the action in a debug build.)

I can now markup any property or method as follows:

[PreProcess(typeof(TracePreProcessor))]
public void DoSomething()
{
 // Do something
}

I also wanted a nice ability to handle exceptions gracefully, which led me to an abstract processor class for this:

public abstract class ExceptionHandlingProcessor : IPostProcessor
{
  public ExceptionHandlingProcessor()
  {
  }

  public void Process(IMethodCallMessage callMsg, 
                ref IMethodReturnMessage retMsg)
  {
    Exception e = retMsg.Exception;
    if(e != null)
    {
      this.HandleException(e);
      Exception newException = this.GetNewException(e);
      if(!object.ReferenceEquals(e,newException))
          retMsg = new ReturnMessage(newException,callMsg);
    }
  }

  public abstract void HandleException(Exception e); 
  public virtual Exception GetNewException(Exception oldException)
  {
    return oldException;
  }
}

This class defers handling of the exception to its derived class, and optionally allows changing of the exception returned. The default behavior is to change the exception for ease of use.

As an aside, I use the Microsoft Exception Management Application Block quite often, you can easily guess what I'll do from now on:

public override void HandleException(Exception e)
{
    ExceptionManager.Publish(e);
}

Then I can just mark every method where I want the exception manager to publish the exception:

[PostProcess(typeof(ExceptionManagerProcessor))]
public void HandleMyException()
{}

For testing, I created two exception processors. The first simply traces it, the second changes the returned exception.

public class TraceExceptionProcessor : ExceptionHandlingProcessor
{
  public TraceExceptionProcessor()
  {}
  public override void HandleException(Exception e)
  {
    Trace.WriteLine(e.ToString());
  }
}

public class ChangeExceptionProcessor : ExceptionHandlingProcessor
{
  public ChangeExceptionProcessor() : base()
  {}
  public override void HandleException(Exception e)
  {
  }
  public override Exception GetNewException(Exception oldException)
  {
    return new ApplicationException("Different");
  }
}

Now, all we need is a test object marked up appropriately:

[Intercept]
public class MyContextObject : ContextBoundObject
{
  public MyContextObject() : base()
  {
  }
  public int MyProperty
  {
    [PreProcess(typeof(TracePreProcessor))]
    get
    {
      return 5;
    }
    [PreProcess(typeof(TracePreProcessor))]
    set
    {
    }
  }
  [PreProcess(typeof(TracePreProcessor))]
  public string DoSomething(string s, int i)
  {
    return s + i.ToString();
  }
  [PreProcess(typeof(TracePreProcessor))]
  [PostProcess(typeof(ChangeExceptionProcessor))]
  [PostProcess(typeof(TraceExceptionProcessor))]
  public void ThrowException()
  {
    throw new ApplicationException("An error");
  }
  public override object InitializeLifetimeService()
  {
    return null;
  }
}

Note that the class inherits from ContextBoundObject and that it is marked with the InterceptAttribute defined above. This will indicate that our Sink wishes to be part of the messaging chain. The methods are marked out with the required processor attributes.

The following is some simple proof of concept test code, with output:

class Class1
{
  /// <summary>
  /// The main entry point for the application.
  /// </summary>
  [STAThread]
  static void Main(string[] args)
  {
    try
    {
      System.Diagnostics.Trace.Listeners.Add( new ConsoleTraceListener() );
      MyContextObject o = new MyContextObject();
      Console.WriteLine(o.DoSomething("str",10));
      o.ThrowException();
    }
    catch(Exception e)
    {
      Console.WriteLine(e);
    }
    Console.ReadLine();
  }
}
PreProcessing:DoSomething
str10
PreProcessing:ThrowException
System.ApplicationException: An error
   at AspectIntercept.MyContextObject.ThrowException() in d:\projects\aspectinte
rcept\aspectintercept.cs:line 221
   at System.Runtime.Remoting.Messaging.Message.Dispatch(Object target, Boolean
fExecuteInContext)
   at System.Runtime.Remoting.Messaging.StackBuilderSink.SyncProcessMessage(IMes
sage msg, Int32 methodPtr, Boolean fExecuteInContext)
System.ApplicationException: Different
Server stack trace:
Exception rethrown at [0]:
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage req
Msg, IMessage retMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgDa
ta, Int32 type)
   at AspectIntercept.MyContextObject.ThrowException() in d:\projects\aspectinte
rcept\aspectintercept.cs:line 221
   at AspectIntercept.Class1.Main(String[] args) in d:\projects\aspectintercept\
class1.cs:line 21

Conclusion

The framework does most of the work here, and provides a number of classes and interfaces that make working with these ideas very easy. One goal of AOP is to make writing software less "connected", and I think that this goes one step to that end.

In addition, I was able to perform all of the tasks that I had in mind:

  1. Log method entry including method name and arguments passed in
  2. Log successful completion (IPostProcessor when exception is null)
  3. Log exception
  4. Optionally change the exception thrown by a method.

Also, performance monitoring became obvious as this simple test demonstrates; I wrote a simple CodeTimer which has a Start and a Finish method - writing the elapsed time to the console when finish is called. Then I wrote a class which implements both IPreProcessor and IPostProcessor:

public class CodeTimerProcessor : IPreProcessor, IPostProcessor
{
  private CodeTimer _timer;
  public CodeTimerProcessor()
  {

  }
#region IPreProcessor Members
  void IPreProcessor.Process(ref IMethodCallMessage msg)
  {
    _timer = new CodeTimer();
    msg.Properties.Add("codeTimer",_timer);
    _timer.Start(msg.MethodName);
  }
#endregion
#region IPostProcessor Members
  void IPostProcessor.Process(IMethodCallMessage callMsg, 
                           ref IMethodReturnMessage retMsg)
  {
    _timer = (CodeTimer)callMsg.Properties["codeTimer"];
    _timer.Finish();
  }
#endregion
}

The crucial line:

msg.Properties.Add("codeTimer",_timer);

stores the timer between the call being made and being returned - PerformanceCounter here I come.

Anyway, this is my first article on CodeProject, I hope it is useful and I look forward to comments and suggestions.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

J4amieC
Software Developer (Senior)
United Kingdom United Kingdom
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralInvoke Object.memberle5ejumeau16 Jul '12 - 20:43 
Hello,
 
Is it possible to get a instance of object in PreProcess or PostProcess method ?
 
Thank you for this source.
GeneralMy vote of 1memberahsan_icra2 May '12 - 21:35 
bal chal
QuestionModifying the call messagememberjackula19 Oct '10 - 21:50 
Hi, sorry for my noob question.
 
My understanding with AOP is that the aspect should be able to intercept the arguments (preprocessing) and the return values (postprocessing). However the arguments and return values respectively are readonly.
 
Is there any way to write to them?
 
Thanks in advance!
GeneralNice, but unusablememberMember 35461354 May '10 - 22:06 
Sorry, but it's unusable, because many methods simply aren't processed. Seems to depend on method's accessor(s), and sometimes, on calling method's accessor. I couldn't figure it out exactly.
GeneralRe: Nice, but unusablememberGary H Guo6 Oct '10 - 5:10 
You may take a look at Add Aspects to Object Using Dynamic Decorator. Please let me know if it is useful to you.
GeneralVery nicemembermikeperetz4 Sep '09 - 18:28 
Just one question, is this going to work if a private method calls another private method, considering the second private method is intercepted...
 
"There are only 10 types of people in the world: Those who understand binary, and those who don't"
More articles on my blog

QuestionFuzzy acronym?memberblackjack215024 Nov '08 - 1:58 
I first thought it stands for Agent Oriented Programming and it took some reading to see it had nothing to do with it. Could you clearly point what AOP and AOSD refer to in this context? Thanks.
AnswerRe: Fuzzy acronym?memberM I developer5 Oct '11 - 2:05 
Aspect Oriented Programming
Generalthere are bright as well as dark sides to this implementation.memberGil.Y23 Nov '07 - 7:32 
The object run in a derived context class, so this basicly kills the purpose of this idea.
 
The implementation is very oriented.
 
The bottom line, I can't use it on a working project.
 
correct me if i'm wrong.
 
Gil.
QuestionAccess to the calling objectmemberKevin Kinnett23 Jul '07 - 5:15 
Is there any way to access the instance of the calling object? I am trying to use this for state verification and would like to be able to somehow access the object.
 
Super cool project!
Thanks very much.
GeneralQuestionmemberMaxGuernsey5 May '06 - 23:22 
It's interesting but I can't find a way to make it work when one method from a given object calls another method on the same object.
 
- Max
GeneralUsing with interfacesmemberMax Samookha10 Jun '05 - 23:15 
Great idea but seems not to work with methods called through an interface.
In PreProcess and PostProcess, msg.MethodBase.GetCustomAttributes returns attributes of the interface method, not those of the method that implements the interface method.
Is there any way to solve the problem?
GeneralRe: Using with interfacesmemberTamerSoul_8 Aug '11 - 13:57 
try this...
    public class InterceptProperty : IContextProperty, IContributeObjectSink
    {
        public InterceptProperty()
            : base()
        {
        }
 
        ...
 
        #region IContributeObjectSink Members
 
        public System.Runtime.Remoting.Messaging.IMessageSink GetObjectSink(MarshalByRefObject obj, System.Runtime.Remoting.Messaging.IMessageSink nextSink)
        {
            var proxy = RemotingServices.GetRealProxy(obj);
            var type = (Type)proxy.GetType().InvokeMember("GetProxiedType", BindingFlags.InvokeMethod, null, proxy, null);
            return new InterceptSink(nextSink, type); // type - class with real implement methods 
        }
 
        #endregion
    }

GeneralStatic methodsmemberRicardo Mendes23 Feb '05 - 2:06 
I've noticed that I can't do the pre and post-processing with static methods. Is there a way to make it work with them?
 
Thanks
GeneralPerformance hitmemberPiers Lawson29 Nov '04 - 2:24 
You mention a performance hit using this technique, and seem to attribute it to having to derive from ContextBoundObject. If your object already has to derive from ContextBoundObject (e.g. it derives from ServicedComponent so as to support COM+) do you know if having a second context significantly degrades performance further? Or is the main performance hit due to the fact you derive from ContextBoundObject regardless of how many "context aware attributes" are then used?
 
I guess I should try it out and see for myself Wink | ;-)
 
Also, I'd be interested to see the code you say you now use e.g. [Trace], [PublishException] etc.
 
Good article.
 
Piers Lawson
GeneralRe: Performance hitmemberJ4amieC29 Nov '04 - 7:55 
Hi Piers,
 
I should have put a disclaimer that I was "no expert on contexts, serviced components or AOP" - I guess this article was driven by the fact I could find lots of related info, but little simple examples of how to put them into use.
 
As far as my current code goes, its not that much different at all, it is however part of a larger commercial product, that uses these ideas to provide some effective code reuse - not much more. As far as the performance goes, the overhead relates down to a bottom line of this; It forms less than 5% of the total time to make my call so I dont worry about it Wink | ;) I usually use only 1 attributes per method (PublishException) and Ive not actually tested what happens to the performance when I add more (of course something like Trace will have its own overhead such as opening file/writing as will a PerformanceMonitor and many other types of attribute)
 
The reason I get good performance from PublishException is that it doeesnt do much at all, and it certainly doesnt do anything processor intensive.
 
I wonder if ive answered any of your questions or just rambled Wink | ;)
 


GeneralRe: Performance hitmemberPiers Lawson30 Nov '04 - 4:20 
J4aimeC
 
Thanks for the prompt reply. I put together a quick and dirty performance test with a number of different classes, each with different attributes.
  • Plain - an ordinary class

  • ContextBound - a class derived directly from ContextBound
  • Intercept - as above with the [Intercept] attribute
  • PreProcessCall - as above with the [PreProcess] attribute on the method

  • Serviced - a class derived from ServicedComponent
  • TransactionRequired - as above with the [Transaction(Transaction.Required)] attribute

  • PreProcessTransaction - a class derived from ServicedComponent with the [Transaction(Transaction.Required)] and [Intercept] attributes as well as the [PreProcess] attribute on the method call.
For each class I looped round 1 million times, instantiating the class and calling a method. My rough and ready timing results are below:

                    Plain 31ms
             ContextBound 62ms
                Intercept 34375ms
           PreProcessCall 34610ms
                 Serviced 110ms 
      TransactionRequired 71203ms
    PreProcessTransaction 71359ms

So it looks like the big overhead is when a class is decorated with a context attribute (in my case either [Intercept] or [Transaction] or both). I presume this is because marking the class means that all calls to it must now go through the remoting mechanisim. Adding additional context attributes does not add much overhead. Adding attributes to individual methods does not incur much additional overhead.
 
Therefore, I may as well use interception on classes that are already marked with the [Transaction] attribute... and as you say, even the worst figure is not greatly significant when compared with the other work being performed.
 
J4amieC wrote:
give me a day and ill get something together
 
I look forward to it.
 
Piers
GeneralRe: Performance hitmemberPiers Lawson2 Dec '04 - 7:05 
What I hadn't considered was the performance hit when a ContextBoundObject creates another ContextBoundObject and calls some method on it. Since your IsContextOK() method does not always return false, that should mean the new object is created in the already existing context and hence calls between the two objects are not intercepted. Therefore I didn't expect there to be a large overhead. I was wrong.
 
I updated my test to have three more cases:
  • CallToPlain - the ContextBound object creates a plain object and calls it;
  • CallToIntercept - the ContextBound object creates an Intercept object which should be in the same context and calls it;
  • CallToIntercept2 - the ContextBound object creates an Intercept2 object (for which the IsContextOK() method of its context attribute always returns false) and calls it;
The results were:

         CallToPlain 35750ms
     CallToIntercept 174469ms
    CallToIntercept2 546531ms

The CallToPlain was much as expected, it took roughly the same time as the Intercept timing from my last post.
 
I was surprised by the time required to instantiate and call another ContextBoundObject that would reside in the same context (i.e. CallToIntercept). Even though the second object was instantiated in the same context, it was taking almost 2 tenths of a second! That is starting to get significant.
 
The results for CallToIntercept2 were very bad. Returning false from IsContextOK() does mean all method calls will be intercepted, but the overhead is now significant, 1/2 a second! this technique is the one used in the MSDN article "Decouple Components by Injecting Custom Services into Your Object's Interception Chain." I don't want to think what it would do with an object that was also marked with the [Transaction(TransactionOption.Required)] attribute... if it would work at all!
 
As an aside... I found that the code:

msg.MethodBase.GetCustomAttributes(typeof(PreProcessAttribute), true)

Doesn't find the attributes if the method is being called via an interface where the interface doesn't have the attribute attached to the method!
 
Piers
GeneralInteresting, but...memberLars Thomas Denstad7 Oct '04 - 1:21 
Interesting, but is this really related to AOP?
 
The main feature of AOP isn't necessarily the ability to do post- or pre-processing, but adding un-suspecting code to a pointcut.
 
In your implementation I fail to see how:
 
[PreProcess(typeof(TracePreprocessor))]
public void Method() {
// ...
}
 
is any better than:
 
public void Method() {
TracePreprocessor.Do();
}
 
Basically you've moved the line of code a little, but it's still basically tied into the original source code. And you're doing the call using mechanisms that imply extra (unneccesary) overhead in addition to burning the base-class.
 
Implementations like AspectJ allow you to modify the runtime abilities of Class.java without modifying Class.java. The "aspects" and "pointcuts" (which methods the aspect applies to) is isolated in a separate file, and Class.java remains untouched. AFAIK, AspectJ allows you to do post/pre-processing on a program where you do not even have access to the source.
 
To reap the benefit of "AOP" I would think an implementation would do seamless post-processing on .cs-files or use reflection/reflection.emit to dynamically analyze and recompile code.
GeneralRe: Interesting, but...memberJ4amieC8 Oct '04 - 10:13 
Thanks for the feedback, your POV is most welcome as you seem to have a good grasp of AOP.
 
I have changed the implementation slightly from what you see above, so the code reads more like:
 
[code]
[Trace]
[PublishException]
public void FacadeMethod()
{}
[/code]
 
Now, this gives an immediate advantages; I can concentrate FacadeMethod on doing whatever its task is, not tracing its input and publishing its exception etc.
 
I guess what I absorbed from the AOP articles etc I read, was that the idea was not to inter-twice methods with tracing with error handling with performance monitoring etc etc, but to "inject" these functions into the code. Now, agreed, the best (readas: most efficient) way to do that is to emit it right into the IL - but thats not what this article was about Wink | ;)
 
Incidentaly, I read alot of people applying this idea to their business objects (or model in a MVC pattern) which I wouldnt personally do. I use this idea in 2 places
 
1) Facades, therein lies the repatative trace-exceptionpublish-performancemonitor-etc functions
2) Data Access Layer, useful for caching and performance monitoring.
 

GeneralRe: Interesting, but...memberLarZiLars9 Oct '04 - 1:08 
My grasp of AOP isn't really that good. Wink | ;)
 
I think AOP the way I outlined it (and the way AspectJ, for example, implements it) does more harm than good. I cannot imagine a horror worse than sitting with a piece of source code, wondering "Why the hell does it do X?", when the code that actually does X it is in another source file being injected into the code in question.
 
The people who presented it to me in the first place had a hard time coming up with examples of problems it actually solves. That is, that it actually solves better than directly modifying the code itself.
 
The best example I where AspectJ actually seems useful was for inspecting and "modifying" code where source is not readily available. (Please, if you could, come up with more examples where AOP, like presented by AspectJ, really solves a problem.)
 
That being said, I really like your article.
GeneralRe: Interesting, but...sussCesar Mercado8 Jan '05 - 10:46 

Lars Thomas Denstad wrote:
To reap the benefit of "AOP" I would think an implementation would do seamless post-processing on .cs-files or use reflection/reflection.emit to dynamically analyze and recompile code.
 
Then you shouldn't compare this 'poor man's' solution to AOP to AspectJ.
 
I should advise you to compare AspectJ to such solutions as AOP.NET, for instance.
 
Cesar Mercado
GeneralRe: Interesting, but...sussCesar Mercado8 Jan '05 - 10:47 

Lars Thomas Denstad wrote:
To reap the benefit of "AOP" I would think an implementation would do seamless post-processing on .cs-files or use reflection/reflection.emit to dynamically analyze and recompile code.
 
Then you shouldn't compare this "poor man's" solution to AOP with AspectJ.
 
I should advise you to compare AspectJ to such solutions as AOP.NET, for instance.
 
Cesar Mercado
GeneralRe: Interesting, but...memberLars Thomas Denstad9 Jan '05 - 4:59 
The point I was trying to make was that this approach reaps none of the benefits of AOP as defined by for example AspectJ or AOP.NET. To me it seems this really isn't related to AOP in the way AOP is usually defined.
GeneralThanks, and an updatememberJ4amieC4 Oct '04 - 9:51 
Thanks for the responses so far, I'm glad the info was at very least ledgible Big Grin | :-D
 
I have changed the above idea slightly in production, as I was a bit unhappy with a few things, mainly:
 
1) I didnt like the way you had to mark Pre & Post Processors, it seemed counter-intuitive in some way
 
2) It is hard, if not impossible, to pass parameters from the attribute to the processor class
 
3) If you wanted the same class to pre & post process you needed two attributes
 
eg.

[PreProcess(typeof(TraceProcessor))]
[PostProcess(typeof(TraceProcessor))]
public void DoSomething()
{}

 
Similar code in my current project would simply be

[MonitorPerformance(MonitorOptions.RequestsPerSecond | MonitorOptions.AverageRequestDuration)]
[PublishException]
[Trace]
public void DoSomething()
{}

 
The attribute itself tells the Intercept classes whether it is a pre or post processor, and clearly allows parameters to be passed in as per the MonitorPerformance attribute above.
 
I will work on ggetting these changes into this article as soon as I have time.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 2 Oct 2004
Article Copyright 2004 by J4amieC
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid