views:

143

answers:

7

I have a bunch of methods with varying signatures. These methods interact with a fragile data connection, so we often use a helper class to perform retries/reconnects, etc. Like so:

MyHelper.PerformCall( () => { doStuffWithData(parameters...) });

And this works fine, but it can make the code a little cluttery. What I would prefer to do is decorate the methods that interact with the data connection like so:

[InteractsWithData]
protected string doStuffWithData(parameters...)
{
     // do stuff...
}

And then essentially, whenever doStuffWithData is called, the body of that method would be passed in as an Action to MyHelper.PerformCall(). How do I do this?

+5  A: 

.NET Attributes are meta-data, not decorators / active components that automatically get invoked. There is not way to achieve this behaviour.

You could use attributes to implement decorators by putting the decorator code in the Attribute class and call the method with a helper method that invokes the method in the Attribute class using Reflection. But I'm not sure this would be a big improvement over just calling the "decorator-method" directly.

"Decorator-Attribute":

[AttributeUsage(AttributeTargets.Method)]
public class MyDecorator : Attribute
{
    public void PerformCall(Action action)
    {
       // invoke action (or not)
    }
}

Method:

[MyDecorator]
void MyMethod()
{
}

Usage:

InvokeWithDecorator(() => MyMethod());

Helper method:

void InvokeWithDecorator(Expression<Func<?>> expression)
{
    // complicated stuff to look up attribute using reflection
}

Have a look at frameworks for Aspect Oriented Programming in C#. These may offer what you want.

dtb
Would you mind typing up a quick code sample of that? I'm having a hard time envisioning it.
mgroves
Example added..
dtb
Awesome, thanks, and I think you're right--I don't really get much benefit over calling the helper directly.
mgroves
+1  A: 

This type of problem is pretty much what AOP (aspect oriented programming) aims to solve. Tools such as PostSharp can provide cross-cutting concerns by re-writing the compiled code. Scott Hanselman's podcast recently discussed AOP, so it might be worth having a listen.

Dave Cluderay
+1  A: 

Check out aspect oriented frameworks. But be aware that while they hide complexity in each method, the existence of AoP features could make your program harder to maintain. It's a tradeoff.

Pontus Gagge
+1  A: 

It seems like what you want is similar to behavior of an IoC container or test runner framework, where it isn't actually executing from your assembly, but is running a dynamically emitted assembly built around your code. (Smarter folks than I have called this AOP in other answers)

So maybe in a stub for your app you could scan the other assemblies, build those emitted assemblies (which call MyHelper.PerformCall with the body of the decorated methods) then your program runs against the emitted code.

By no means would I start down the road of trying to write this without evaluating whether some existing AOP framework could accomplish what you need. HTH>

JohnKeller
A: 

Seeing that you're willing to add a line of code to every method that needs this, why not just make the call to MyHelper from within the method itself, like this?

protected string doStuffWithData(parameters...)
{
     MyHelper.PerformCall( () => { doStuffWithDataCore(parameters...) });
}

private string doStuffWithDataCore(parameters...) {
    //Do stuff here
}
Sijin
Yeah, that's what I'm currently doing.
mgroves
Then what does adding the attribute do for you?
Sijin
I think it might make the code easier to read and maintain.
mgroves
+1  A: 

Without the use of gode generation, you can't do much against it. You could probably make the syntax better.

But what about using an extension method?

class static MyHelper
{
  Wrap<T>(this object service, Action<T> action)
  {
    // check attribute and wrap call
  }

}

usage:

RawFoo foo = ...
foo.Wrap(x => x.doStuffWithData(parameters...));

This is trivial, but you can't make sure that Wrap had been used.

You could implement a generic decorator. This decorator would be used once to wrap the service and then you can't call it without wrapping.

class Decorator<T>
{
    private T implementor;

    Decorator(T implementor)
    {
      this.implementor = implementor;
    }

    void Perform<T>(Action<T> action)
    {
      // check attribute here to know if wrapping is needed
      if (interactsWithData)
      {
        MyHelper.PerformCall( () => { action(implementor) });
      }
      else
      {
        action(implementor);
      }
    }
}

static class DecoratorExtensions
{
    public static Decorator<T> CreateDecorator<T>(T service)
    {
      return new Decorator<T>(service);
    }
}

Usage:

// after wrapping, it can't be used the wrong way anymore.
ExtendedFoo foo = rawFoo.CreateDecorator();
foo.Perform(x => x.doStuffWithData(parameters...));
Stefan Steinegger
A: 

So, I just went to a AOP session this weekend, and here's a way to do it with PostSharp:

[Serializable]
public class MyAOPThing : MethodInterceptionAspect
{
    public override void OnInvoke(MethodInterceptionArgs args)
    {
        Console.WriteLine("OnInvoke! before");
        args.Proceed();
        Console.WriteLine("OnInvoke! after");
    }
}

And then decorate methods with [MyAOPThing]. Easy!

mgroves