Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Basic implementation of AOP like attribute using standard .NET Framework [duplicate]

Possible Duplicate:
C# wrap method via attributes

I'd like to achieve such functionality:

[Atomic]
public void Foo()
{           
    /* foo logic */
}

Where [Atomic] attribute is an attribute, which wraps function logic within a transaction scope:

using(var scope = new TransactionScope())
{
    /* foo logic */
    scope.Complete();
}

How to write such an attribute?

I've asked before basically the same question, I know this can be done using AOP, but I didn't mention I'm searching for some simplest proof of concept implementation or helpful articles which can help me to write this using pure .NET Framework (I suppose using RealProxy and MarshalByRefObject types, about which I've read browsing related questions).

I need to solve exactly this shown example. It seems like a basic thing so I want to learn how to do it starting from scratch. It doesn't need to be safe and flexible for now.

like image 366
jwaliszko Avatar asked Jan 13 '13 19:01

jwaliszko


3 Answers

It seems like a basic thing...

It's one of the (many) things which are simple to understand the concept, but not at all simple to implement.

As per Oded's answer, Attributes in .NET don't do anything. They only exist so that other code (or developers) can look at them later on. Think of it as a fancy comment.

With that in mind, you can write your attribute like this

public class AtomicAttribute : Attribute { } 

Now the hard part, you have to write some code to scan for that attribute, and change the behaviour of the code.

Given that C# is a compiled language, and given the rules of the .NET CLR there are theoretically 3 ways to do this

  1. Hook into the C# compiler, and make it output different code when it sees that attribute.
    This seems like it would be nice, but it is simply not possible right now. Perhaps the Roslyn project might allow this in future, but for now, you can't do it.

  2. Write something which will scan the .NET assembly after the C# compiler has converted it to MSIL, and change the MSIL.
    This is basically what PostSharp does. Scanning and rewriting MSIL is hard. There are libraries such as Mono.Cecil which can help, but it's still a hugely difficult problem. It may also interfere with the debugger, etc.

  3. Use the .NET Profiling API's to monitor the program while it is running, and every time you see a function call with that attribute, redirect it to some other wrapper function.
    This is perhaps the simplest option (although it's still very difficult), but the drawback is that your program now must be run under the profiler. This may be fine on your development PC, but it will cause a huge problem if you try deploy it. Also, there is likely to be a large performance hit using this approach.

In my opinion, your best bet is to create a wrapper function which sets up the transaction, and then pass it a lambda which does the actual work. Like this:

public static class Ext 
{
    public static void Atomic(Action action) 
    {
        using(var scope = new TransactionScope()) 
        {
            action();
            scope.Commit();
        }
    }
}

.....

using static Ext; // as of VS2015

public void Foo()
{
    Atomic(() => {
        // foo logic
    }
}

The fancy computer science term for this is Higher order programming

like image 155
Orion Edwards Avatar answered Oct 07 '22 19:10

Orion Edwards


Attributes are meta data - that's all they are.

There are many tools that can take advantage of such metadata, but such tooling needs to be aware of the attribute.

AOP tools like PostSharp read such metadata in order to know what and where to weave aspects into code.

In short - just writing an AtomicAttribute will give you nothing - you will need to pass the compiled assembly through a tool that knows about this attribute and do "something" to it in order to achieve AOP.

like image 22
Oded Avatar answered Oct 07 '22 18:10

Oded


It is not a basic thing at all. No extra code is run just because a method has an attribute, so there is nowhere to put your TransactionScope code.

What you would need to do is at application start-up use reflection to iterate over every method on every class in your assembly and find the methods that are marked with AtomicAttribute, then write a custom proxy around that object. Then somehow get everything else to call your proxy instead of the real implementation, perhaps using a dependency injection framework.

Most AOP frameworks do this at build time. PostSharp for example runs after VisualStudio builds your assembly. It scans your assembly and rewrites the IL code to include the proxies and AOP interceptors. This way the assembly is all set to go when it is run, but the IL has changed from what you originally wrote.

like image 4
CodingWithSpike Avatar answered Oct 07 '22 20:10

CodingWithSpike