Sunday, January 6, 2008

A Familiar Story, Told by LinFu.AOP

Here's a simple story that shows what one can do with LinFu.AOP. Note the code listing below:

public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public void Speak()
{
Console.WriteLine("Hello, World!");
}
}

public class Demon
{
public string Name
{
get { return "Captain Howdy"; }
set { throw new InvalidOperationException("Are you crazy??"); }
}
public int Age
{
get { return 999999; }
set { }
}
public void Possess(Person person)
{
DynamicObject dynamic = new DynamicObject(this);
dynamic.Attach(person);
}
public void Deposses(Person person)
{
MakeHeadSpin();
VomitGreenStuff();
throw new InvalidOperationException("Fat chance, buddy!");
}
public void MakeHeadSpin()
{
// ...
}
public void VomitGreenStuff()
{
// ...
}
public void Speak()
{
Console.WriteLine("This girl is mine!");
}
}
class Program
{
static void Main(string[] args)
{
// There once was a nice little girl...
Person girl = new Person() { Name = "Linda Blair", Age = 6 };

// Who spoke in a soft voice..
girl.Speak();

// Until one day, she played with some crazy oija board named 'LinFu.AOP', and
// then everything changed...
Demon demon = new Demon();
demon.Possess(girl);
IModifiableType modified = girl;
girl.IsInterceptionEnabled = true;

// Then some devoted priest came along and spoke with the girl, and she said:
girl.Speak();

// He took pity on the girl, and tried to free her from the demon.
demon.Deposses(girl);

// The demon resisted, but the priest was smart, and in the end,
// he subdued the demon.
girl.IsInterceptionEnabled = false;


// ..the end
return;
}
}

The Devil in the Details

Now, if you look at that code listing above, you'll notice that the Person class has no idea that it's being intercepted at all. In fact, poor little Linda doesn't know the trouble she's getting into. The Demon class was able to possess Person types because LinFu.AOP modified the Person class to allow itself to be dynamically intercepted at runtime, all without the knowledge of the said victim.

What makes this interesting, however, is that interception can be easily turned on or off depending on the value of the IsInterceptionEnabled property. In fact, if interception is turned off, the Person class will behave just like any other normal person without any performance penalties that are typically incurred by pass-through interception. If interception is turned on, of course, you'll be bound to have some performance overhead incurred, but the added flexibility bonus is staggering. With the slight decrease in performance, you can effectively take over the implementation of the entire class at runtime, and you can even customize it on a per-instance basis.

Think about it. :)

4 comments:

  1. Really, really cool.. I use postSharp for a few things currently but the flexibility of LinFu.AOP sounds like it will really take things to another level.

    What kind of performance penalty is incurred for runtime interception?

    Also, coming to SVN soon? =)

    ReplyDelete
  2. The performance penalty will depend on what you decide to replace/surround with each method as the app is running.

    If runtime interception is disabled, the only performance penalty that will be incurred is the additional cost of a single virtual method call to IsInterceptionEnabled.

    If, however, you decide to completely 'gut' a method and surround it with your own custom behavior, you can expect some slight overhead at the cost of about 8-10 additional method calls,

    At this point, however, I haven't quite benchmarked it yet, but I'll make it a point to put some benchmarks in the article for a good comparison.

    As for an SVN release--LinFu.AOP will be released on SVN once I get the article out. I might even post an article preview (once it's done) so that everyone can get an idea of what it does. HTH!

    ReplyDelete
  3. I'm about 1/3 way through your part VI article, so I hope I'm not jumping the gun and my question is actually answered later. But, I have an SOA framework that I've developed for work and need to eventually add authorization to the client invocations. This is all using .NET remoting. I have gone part way down the road of creating custom sinks to handle the metadata passed along with each remote invocation. We also have a license for GenuineChannels that I will be investigating. But, I was wondering if LinFu.AOP could not also be used in place of my custom sinks. After all, the custom sink chains are simply allowing me to insert my code into the invocation chain on each side of the remote method.

    Just a thought. I was wondering if you ever had to the same one. What do you think are my odds of success?

    ReplyDelete
  4. @dviljoen:

    You can probably use LinFu.AOP to transparently inject client authorization before each method call. In theory, all you have to do is tell LinFu.AOP which methods you want to intercept, and it will take care of all the injection details for you. I haven't worked much with .NET remoting per se, but I think it's safe to say that LinFu.AOP's approach will be much faster than inserting your code through remoting chains since LinFu does all its injection at the IL level (rather than having to go through so many custom sinks on both sides of the wire). If you're looking for an "off the cuff" benchmark, there's really no comparison between raw IL (LinFu's approach) versus .NET Remoting sinks. Suffice to say, anyone who's ever done a speed comparison between using a RealProxy from the .NET BCL vs a dynamic proxy emitted in IL will attest to that :)

    Anyway, before I inadvertently go off into a tangent, the short answer to your question is this: Yes, you can do it through LinFu.AOP, and it makes it rediculously easy to inject additional code into your app, even if the application is already compiled. So go for it :)

    ReplyDelete

Ratings by outbrain