All Projects → vescon → MethodBoundaryAspect.Fody

vescon / MethodBoundaryAspect.Fody

Licence: MIT license
A Fody weaver which allows to decorate methods and hook into method start, method end and method exceptions.

Programming Languages

C#
18002 projects

Projects that are alternatives of or similar to MethodBoundaryAspect.Fody

Mimick.Fody
An integrated framework for dependency injection and aspect-oriented processing.
Stars: ✭ 15 (-91.33%)
Mutual labels:  aop, fody
Cauldron
C# Toolkit
Stars: ✭ 68 (-60.69%)
Mutual labels:  aop, fody
Ray.di
Guice style dependency injection framework for PHP
Stars: ✭ 175 (+1.16%)
Mutual labels:  aop
Deflector.NET
A library for intercepting all method calls at runtime in nearly any .NET application.
Stars: ✭ 80 (-53.76%)
Mutual labels:  aop
Bear.sunday
A resource-oriented application framework
Stars: ✭ 230 (+32.95%)
Mutual labels:  aop
Htframework
Unity HTFramework, a rapid development framework of client to the unity.
Stars: ✭ 179 (+3.47%)
Mutual labels:  aop
controller-logger
AOP based API logging for Spring Boot
Stars: ✭ 57 (-67.05%)
Mutual labels:  aop
Alipay Sdk Php
🐜支付宝(蚂蚁金服)开放平台第三方 PHP SDK,基于官方 3.3.2 版本,助力支付宝小程序后端开发。
Stars: ✭ 167 (-3.47%)
Mutual labels:  aop
hasor
Hasor是一套基于 Java 语言的开发框架,区别于其它框架的是 Hasor 有着自己一套完整的体系,同时还可以和先有技术体系做到完美融合。它包含:IoC/Aop容器框架、Web框架、Jdbc框架、RSF分布式RPC框架、DataQL引擎,等几块。
Stars: ✭ 938 (+442.2%)
Mutual labels:  aop
Coldbox Platform
A modern, fluent and conventions based HMVC framework for ColdFusion (CFML)
Stars: ✭ 220 (+27.17%)
Mutual labels:  aop
langx-java
Java tools, helper, common utilities. A replacement of guava, apache-commons, hutool
Stars: ✭ 50 (-71.1%)
Mutual labels:  aop
Nutz
Nutz -- Web Framework(Mvc/Ioc/Aop/Dao/Json) for ALL Java developer
Stars: ✭ 2,422 (+1300%)
Mutual labels:  aop
Swoft Im
基于swoft-cloud的微服务架构,最小化拆分粒度,PHP7、多进程、协程、异步任务、mysql连接池、redi连接池、rpc连接池、服务治理、服务注册与发现、Aop切面、全注解
Stars: ✭ 189 (+9.25%)
Mutual labels:  aop
Norns
dotnet core aop static weaving on roslyn
Stars: ✭ 23 (-86.71%)
Mutual labels:  aop
Click Debounce
Using ASM to handle Android's click debounce, specially a quick double click.
Stars: ✭ 175 (+1.16%)
Mutual labels:  aop
Lokie
iOS efficient AOP Library using C++ and libffi
Stars: ✭ 139 (-19.65%)
Mutual labels:  aop
Pine
Dynamic java method hook framework on ART.
Stars: ✭ 171 (-1.16%)
Mutual labels:  aop
Dntframeworkcore
Lightweight and Extensible Infrastructure for Building Web Applications - Web Application Framework
Stars: ✭ 208 (+20.23%)
Mutual labels:  aop
Mradvice
.NET aspect weaver (build task under NuGet package)
Stars: ✭ 236 (+36.42%)
Mutual labels:  aop
NetCoreBeauty
Move a .NET Framework/.NET Core app runtime components and dependencies into a sub-directory and make it beauty.
Stars: ✭ 228 (+31.79%)
Mutual labels:  fody

.github/workflows/main.yaml NuGet

MethodBoundaryAspect.Fody

A Fody weaver which allows to decorate methods and hook into method start, method end and method exceptions. Additionally you have access to useful method parameters.

You can easily write your own aspects for

  • transaction handling
  • logging
  • measuring method execution time
  • exception wrapping
  • displaying wait cursor
  • and much more ...

Supported features

  • Hook into method start and end
  • Hook into raised exceptions in a method
  • Access method information like MethodExecutionArgs
    • applied object instance
    • the method itself (System.Reflection.MethodBase)
    • the passed method arguments
    • the thrown exception
    • some custom object, which can be set at the start of the method and accessed at the end (e.g. useful for transactions or timers)
  • Apply aspects at different levels
    • globally in AssemblyInfo.cs
    • on class
    • on method
  • Change method behavior (see examples below)
    • Overwrite input arguments values (byValue and byRef) to be forwarded to the method.
    • Overwrite return value to be returned from the method.

Feel free to make a Fork of this repository.

Quickstart

  1. Install the MethodBoundaryAspect.Fody NuGet package (Install-Package MethodBoundaryAspect.Fody)
  2. Create FodyWeavers.xml in your project and add the Weaver MethodBoundaryAspect to it (further details)
  3. Write your custom aspects by deriving from OnMethodBoundaryAspect and decorate your methods (see sample below)

Sample

Short sample how a transaction aspect could be implemented.

The aspect code

using MethodBoundaryAspect.Fody.Attributes;

public sealed class TransactionScopeAttribute : OnMethodBoundaryAspect
{
    public override void OnEntry(MethodExecutionArgs args)
    {
        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();
    }
}

The applied aspect

public class Sample
{
    [TransactionScope]
    public void Method()
    {
        Debug.WriteLine("Do some database stuff isolated in surrounding transaction");
    }
}

Additional Sample

Consider an aspect written like this.

The aspect code

using static System.Console;
using MethodBoundaryAspect.Fody.Attributes;

public sealed class LogAttribute : OnMethodBoundaryAspect
{
    public override void OnEntry(MethodExecutionArgs args)
    {
        WriteLine("On entry");
    }

    public override void OnExit(MethodExecutionArgs args)
    {
        WriteLine("On exit");
    }

    public override void OnException(MethodExecutionArgs args)
    {
        WriteLine("On exception");
    }
}

The applied aspect

Suppose the aspect is applied to this method:

using static System.Console;

public class Sample
{
    [Log]
    public void Method()
    {
        WriteLine("Entering original method");
        OtherClass.DoSomeOtherWork();
        WriteLine("Exiting original method");
    }
}

We would expect the method call to output this:

On entry
Entering original method
Exiting original method
On exit

If, however, the call to OtherClass.DoSomeOtherWork() throws an exception, then it will look like this instead:

On entry
Entering original method
On exception

Note that the OnExit handler is not called when an exception is thrown.

Altering Method Behavior

Changing return values

In order to change the return value of a method, hook into its OnExit handler and set the ReturnValue property of the MethodExecutionArgs.

using MethodBoundaryAspect.Fody.Attributes;
using System;
using System.Linq;
using static System.Console;

public sealed class IndentAttribute : OnMethodBoundaryAspect
{
    public override void OnExit(MethodExecutionArgs args)
    {
        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";
    }
}

This can also be used on async methods to add additional processing or exception-handling.

using MethodBoundaryAspect.Fody.Attributes;
using System;
using System.Linq;
using static System.Console;

public sealed class HandleExceptionAttribute : OnMethodBoundaryAspect
{
    public override void OnExit(MethodExecutionArgs args)
    {
        if (args.ReturnValue is Task<string> task)
        {
          args.ReturnValue = task.ContinueWith(t =>
          {
            if (t.IsFaulted)
              return "An error happened: " + t.Exception.Message;
            return t.Result;
          });
        }
    }
}

public class Program
{
    [HandleException]
    public static async Task<string> Process()
    {
      await Task.Delay(10);
      throw new Exception("Bad data");
    }

    public static async Task Main(string[] args)
    {
        WriteLine(await Process()); // Output: "An error happened: Bad data"
    }
}

Changing input arguments

In order to change the return value of a method, hook into its OnEntry handler and modify the elements of the Arguments property of the MethodExecutionArgs.
Important:
And you have to annotate your aspect with the AllowChangingInputArgumentsAttribute because the weaver has to generate additional code. For non-modifying aspects this code is unnecessary and would only cost performance. No async support yet!

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;
        }
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        // ByValue
        MethodByValue(10);

        // ByRef
        var value = 10;
        MethodByRef(ref value);
        Console.WriteLine("after method call: " + value); // Output: 20
    }

    [InputArgumentIncrementor(Increment = 1)]
    public static void MethodByValue(int i)
    {
        Console.WriteLine(i); // Output: 11
    }

    [InputArgumentIncrementor(Increment = 10)]
    public static void MethodByRef(ref int i)
    {
        Console.WriteLine(i); // Output: 20
    }
}

Asynchronous Sample

Consider the same aspect as above but now applied to this method:

using static System.Console;

public class Sample
{
    [Log]
    public async Task MethodAsync()
    {
        WriteLine("Entering original method");
        await OtherClass.DoSomeOtherWorkAsync();
        WriteLine("Exiting original method");
    }
}

The On entry line will be written when MethodAsync is first called on the main thread.

The Entering original method line will be written shortly thereafter, on the main thread.

The Exiting original method line will be written only after the task returned by OtherClass.DoSomeOtherWorkAsync has completed. Depending on your context and whether the given task was already complete when it was returned, this may or may not be on the main thread.

The On exit line will be written when MethodAsync returns to its caller, which may be slightly after or long before the long-running task has completed, but it will occur synchronously on the main thread.

The On exception line will be written if OtherClass.DoSomeOtherWorkAsync throws an exception, returns null (thus causing a NullReferenceException when it is awaited), or returns a faulted task. As such, this may occur long after MethodAsync itself has returned its Task to its caller. The call to OnException will take place on whichever thread the synchronization context was running when the exception occurred.

Note that, unlike for synchronous methods, an aspect for an asynchronous method will have its OnExit handler called whether or not its OnException is called. Furthermore, unlike synchronous methods, the call to OnException may take place long after the call to OnExit. If this behavior is undesirable, consider using the MethodExecutionTag to track whether the OnExit has run before the OnException. One such solution looks like this.

using static System.Console;
using MethodBoundaryAspect.Fody.Attributes;

public sealed class LogAttribute : OnMethodBoundaryAspect
{
    public override void OnEntry(MethodExecutionArgs args)
    {
        WriteLine("On entry");
        arg.MethodExecutionTag = false;
    }

    public override void OnExit(MethodExecutionArgs args)
    {
        WriteLine("On exit");
        arg.MethodExecutionTag = true;
    }

    public override void OnException(MethodExecutionArgs args)
    {
        if ((bool)arg.MethodExecutionTag)
          return;
        WriteLine("On exception");
    }
}

One additional note about the asynchronous behavior: the OnExit handler runs when the MethodAsync returns to its caller, not when the asynchronous code finishes running, which may be some time later. If you need code to be run when the method's asynchronous code finishes instead of when the actual method exits, consider a solution like the following:

using static System.Console;
using MethodBoundaryAspect.Fody.Attributes;

public sealed class LogAttribute : OnMethodBoundaryAspect
{
    public override void OnEntry(MethodExecutionArgs args)
    {
        WriteLine("On entry");
    }

    public override void OnExit(MethodExecutionArgs args)
    {
        if (args.ReturnValue is Task t)
            t.ContinueWith(task => WriteLine("On exit"));
    }

    public override void OnException(MethodExecutionArgs args)
    {
        WriteLine("On exception");
    }
}

Benchmarks

  • BenchmarkDotNet=v0.13.2, OS=Windows 10 (10.0.19043.1766/21H1/May2021Update)
  • Intel Core i7-10700K CPU 3.80GHz, 1 CPU, 16 logical and 8 physical cores
  • .NET SDK=7.0.100
    • [Host] : .NET 7.0.0 (7.0.22.51805), X64 RyuJIT AVX2
    • DefaultJob : .NET 7.0.0 (7.0.22.51805), X64 RyuJIT AVX2
Method Mean Error StdDev
CallWithoutAspect 108.8 ns 0.50 ns 0.44 ns
CallWithAspect 136.3 ns 2.72 ns 3.98 ns
OpenGenericCallWithoutAspect 105.5 ns 1.03 ns 0.91 ns
OpenGenericCallWithAspect 201.0 ns 3.37 ns 2.82 ns
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].