18

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.

Community
  • 1
  • 1
jwaliszko
  • 16,942
  • 22
  • 92
  • 158
  • If you're doing this for your own education, that's fine, but if you're doing this to solve a business requirement, then it's already been done for you in WCF and COM+. – Christian Hayter Jan 13 '13 at 20:17

4 Answers4

23

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

Community
  • 1
  • 1
Orion Edwards
  • 121,657
  • 64
  • 239
  • 328
  • Thanks for your time. I'm starting to get now what actually I've really asked about. Regards. – jwaliszko Jan 13 '13 at 22:44
  • It's not exactly true that Attributes don't do anything. Look at System.Web.Http.AuthorizeAttribute which handles authorization of a request's IPrincipal. – Polymorphix Feb 20 '14 at 08:26
  • 2
    @Polymorphix Nope, System.Web.Http.AuthorizeAttribute also doesn't do anything. When ASP.NET Web API is doing it's frameworky stuff and creating instances of your classes, it looks for AuthorizeAttribute, and acts differently if it sees it, but the attribute itself just sits there waiting for someone to look for it – Orion Edwards May 01 '14 at 01:09
  • @OrionEdwards Yes, you're absolutely right. My bad. – Polymorphix May 02 '14 at 13:10
  • @OrionEdwards - any clues on how to nest the scope? Say I have another method w/c calls Ext.Atomic and within that i will call Foo. – james Feb 16 '17 at 06:40
  • @james I'm not sure I understand what you're asking. There's nothing to stop you calling Atomic inside the nested lambda. Whether or not this *does anything sensible* depends on how you've chosen to implement your `Atomic` or whatever you've called it method – Orion Edwards Feb 22 '17 at 00:27
10

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.

Oded
  • 489,969
  • 99
  • 883
  • 1,009
4

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.

CodingWithSpike
  • 42,906
  • 18
  • 101
  • 138
  • I'd like to get into this area with some details to extend my perception about the Framework and do some basic stuff you've mentioned about in the second paragraph. Thanks for answer. – jwaliszko Jan 13 '13 at 22:48
3

Maybe resolve all objects using IoC container? You could configure interceptors for your types and in them check if called method is decorated with that attribute. You could cache that information so that you don't have to use reflection on every method call.

So when you do this:

var something = IoC.Resolve<ISomething>();

something is not object you have implemented but proxy. In that proxy you can do whatever you want before and after the method call.

Piotr Perak
  • 10,718
  • 9
  • 49
  • 86
  • I think modern programs use a IOC container. So, this is the way to go. Have a look at https://stackoverflow.com/questions/2592787/what-really-interceptors-do-with-my-c-sharp-class which does exactly AOP with a proxy, and intersceptors annotations on a method, and do the work. In combi with a IOC container this works as a charm. I'll attach some real code later, as I need is now... – Roland Roos Mar 16 '20 at 14:14