MethodBoundaryAspect.Fody

repository·master·Indexed 44 days ago

https://github.com/vescon/methodboundaryaspect.fody

A Fody weaver for injecting cross-cutting concerns such as logging, transactions, or performance monitoring into assemblies, classes, or methods. It allows developers to implement custom aspects by deriving from OnMethodBoundaryAspect and overriding OnEntry, OnExit, and OnException to manage method lifecycles, modify input arguments via [AllowChangingInputArguments], and handle asynchronous method execution.

Tokens
1.8K
Snippets
5
Records
5
Agent score
5%

What's inside MethodBoundaryAspect.Fody

  1. Understand asynchronous aspect behavior

    master

    When applying aspects to async methods, note the following lifecycle differences:

    1. OnEntry: Runs on the main thread when the method is first called.
    2. OnExit: Runs when the method returns its Task to the caller, not necessarily when the asynchronous work completes. To run code when the work actually finishes, use task.ContinueWith inside OnExit.
    3. OnException: Triggered if the method throws, returns null (causing a NullReferenceException on await), or returns a faulted task. This may occur long after OnExit has run.

    Tip: Because OnExit and OnException can overlap or run in unexpected orders in async contexts, use MethodExecutionTag to track if OnExit has already executed to avoid duplicate logic in OnException.

    // Pattern to ensure OnException only runs if OnExit hasn't
    public sealed class LogAttribute : OnMethodBoundaryAspect
    {
        public override void OnEntry(MethodExecutionArgs args)
        {
            args.MethodExecutionTag = false;
        }
    
        public override void OnExit(MethodExecutionArgs args)
        {
            args.MethodExecutionTag = true;
        }
    
        public override void OnException(MethodExecutionArgs args)
        {
            if ((bool)args.MethodExecutionTag) return;
            // Handle exception...
        }
    }
  2. Implement a custom aspect using OnMethodBoundaryAspect

    master

    To create an aspect, inherit from OnMethodBoundaryAspect and override the lifecycle methods: OnEntry, OnExit, and OnException. You use the MethodExecutionArgs object to access method details and state.

    Example of a transaction aspect that uses MethodExecutionTag to pass state between entry and exit:

    using MethodBoundaryAspect.Fody.Attributes;
    
    public sealed class TransactionScopeAttribute : OnMethodBoundaryAspect
    {
        public override void OnEntry(MethodExecutionArgs args)
        {
            // Set a custom object to be accessed later
            args.MethodExecutionTag = new TransactionScope();
        }
    
        public override void OnExit(MethodExecutionArgs args)
        {
            var transactionScope = (TransactionScope)args.MethodExecutionTag;
            transactionScope.Complete();
            transactionScope.Dispose();
        }
    
        public override void OnException(MethodExecutionArgs args)
        {
            var transactionScope = (TransactionScope)args.MethodExecutionTag;
            transactionScope.Dispose();
        }
    }
  3. Install and setup MethodBoundaryAspect.Fody

    master

    To use MethodBoundaryAspect.Fody, follow these three steps:

    1. Install the NuGet package: Run Install-Package MethodBoundaryAspect.Fody in your package manager console.
    2. Configure Fody: Create a FodyWeavers.xml file in your project root and add MethodBoundaryAspect to it.
    3. Implement Aspects: Create custom aspect classes by deriving from OnMethodBoundaryAspect and apply them using attributes.
    Install-Package MethodBoundaryAspect.Fody
  4. Change method return values in OnExit

    master

    You can overwrite the return value of a method by setting the ReturnValue property of MethodExecutionArgs within the OnExit handler. This works for both synchronous and asynchronous methods.

    For asynchronous methods, you can intercept the returned Task to perform additional processing or exception handling using ContinueWith.

    using MethodBoundaryAspect.Fody.Attributes;
    using System;
    using System.Linq;
    using static System.Console;
    
    public sealed class IndentAttribute : OnMethodBoundaryAspect
    {
        public override void OnExit(MethodExecutionArgs args)
        {
            // Overwrite the return value
            args.ReturnValue = String.Concat(args.ReturnValue.ToString().Split('\n').Select(line => "  " + line));
        }
    }
    
    public class Program
    {
        [Indent]
        public static string GetLogs() => @"Detailed Log 1
    Detailed Log 2";
    
        public static void Main(string[] args)
        {
            WriteLine(GetLogs()); // Output: "  Detailed Log 1\n  Detailed Log 2";
        }
    }
  5. Change method input arguments in OnEntry

    master

    To modify input arguments (both byValue and byRef), modify the elements of the args.Arguments array within the OnEntry handler.

    Critical Requirement: You MUST annotate your aspect class with [AllowChangingInputArguments] to enable this behavior. This feature does not support asynchronous methods.

    using System;
    using MethodBoundaryAspect.Fody.Attributes;
    
    [AllowChangingInputArguments]
    public sealed class InputArgumentIncrementorAttribute : OnMethodBoundaryAspect
    {
        public int Increment { get; set; }
    
        public override void OnEntry(MethodExecutionArgs args)
        {
            var inputArguments = args.Arguments;
            for (var i = 0; i < inputArguments.Length; i++)
            {
                var value = inputArguments[i];
                if (value is int v)
                    inputArguments[i] = v + Increment;
            }
        }
    }