mercredi 8 janvier 2020

Create an Attribute which will treat the decorated method as a delegate

Preamble/Context

So currently I have a Log method.

public T Log<T>(Func<T> func)
{
    string methodName = func.Method.Name;
    bool success = false;
    T product = default(T);

    Debug($"Entering {methodName}");

    try
    {
        product = func.Invoke();
        success = true;
    }
    catch (Exception e)
    {
        Info($"FAILURE: {methodName}: {e.Message}");
    }

    Debug($"{success.ToString().ToUpper()}: Exiting {methodName}");
    return product;
}

Which works really well for me and is called like this

Log(() => AddTwoNumbers(1, 2));

I've been using the decorator pattern and basically saying that all the methods of the interface call their base (or the contained object or however you want to go about implementing the base method) so that the methods are logged.

DISCLAIMER I don't have easy access to AOP in my situation so generating a proxy that handles this isn't an easy route. I'm mostly overhauling legacy code and making calls to my .NET libraries from Visual Basic 6 EXEs and from what I can see most of these AOP frameworks need to be incorporated directly into the app.

The Problem I'm Trying to Solve

Many times I find myself doing this:

public int AddFirstFourNumbers(params int[] numbahs) 
{
  Log(() => 
  {
    int product;
    product += numbahs[0];
    product += numbahs[1];
    product += numbahs[2];
    product += numbahs[3];

    return product;
  }
}

Or decorating all members of an extension class like this

public override int AddTwoNumbers(int num1, int num2)
{
  return Log(() => base.AddTwoNumbers(num1, num2));
}

and I'd like to turn that into this:

[Log]
public int AddFirstFourNumbers(params int[] numbahs) 
{
    int product;
    product += numbahs[0];
    product += numbahs[1];
    product += numbahs[2];
    product += numbahs[3];

    return product;
}

and this

[Log]
public override int AddTwoNumbers(int num1, int num2)
{
  return base.AddTwoNumbers(num1, num2);
}

I'm not very familiar with creating custom Attributes but is there a way to treat the method decorated by this log attribute as a delegate and pass it to the Log method described in the first block? The examples I see on the Microsoft Docs don't seem to do anything close to what I'm hoping for.

https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/attributes/creating-custom-attributes

At the same time we have the MVC validation attributes which seem to provide tons of helpful functionality so this may yet be possible.

I'm a huge fan of Action and Func Wrappers that allow me to decorate my base methods and separate out my concerns. I'm just looking for a cleaner way of doing so. It would be nice to be able to just add [WithImpersonation] to the top of a method or [UndoOnFailure] and only display the core implementation within the actual method block.





Aucun commentaire:

Enregistrer un commentaire