GithubHelp home page GithubHelp logo

vescon / methodboundaryaspect.fody Goto Github PK

View Code? Open in Web Editor NEW
234.0 14.0 74.0 8.4 MB

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

License: MIT License

C# 100.00%
fody aop hacktoberfest fody-weaver

methodboundaryaspect.fody's Introduction

.github/workflows/main.yaml NuGet

MethodBoundaryAspect.Fody

A Fody weaver which allows to decorate assemblies, classes and 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
  • Filter which methods to include in weaving, using Regex patterns
    • NamespaceFilter
    • TypeNameFilter
    • MethodNameFilter
  • 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

methodboundaryaspect.fody's People

Contributors

chloeffl avatar dev-anton avatar erikbrgr avatar geirludvigsen avatar iandawson avatar jeffward01 avatar jmartschinke avatar keith-anders avatar luispfgarces avatar marcells avatar martinay avatar nilfusion avatar ralf1108 avatar timonsdad avatar tom-englert avatar wautvda avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

methodboundaryaspect.fody's Issues

Can not add arguments to OnMethodBoundaryAspect attribute

If I try and add an initializer or constructor parameters to an OnMethodBoundaryAspect derived attribute, the attribute breaks the build.

For example, given something like :

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
    public class ExceptionAttribute : OnMethodBoundaryAspect
    {
        public Type ExceptionType { get; set; }
        public string TranslationKey { get; set; }
    }

If I try and decorate a method like this :

[ExceptionAttribute(TranslationKey = "fun")]
public void Method()

The build fails with this stack :

1>------ Build started: Project: MethodBoundaryAspect.Tests, Configuration: Debug Any CPU ------
1>    Fody: Fody (version 1.29.2.0) Executing
1>MSBUILD : error : Fody: An unhandled exception occurred:
1>MSBUILD : error : Exception:
1>MSBUILD : error : Parametertype: System.String
1>MSBUILD : error : StackTrace:
1>MSBUILD : error :    at MethodBoundaryAspect.Fody.InstructionBlockCreator.LoadConstOnStack(TypeReference parameterType, Object value)
1>MSBUILD : error :    at MethodBoundaryAspect.Fody.InstructionBlockCreator.NewObject(VariableDefinition newInstance, TypeReference instanceTypeReference, ModuleDefinition module, CustomAttribute aspect, Int32 aspectCounter)
1>MSBUILD : error :    at MethodBoundaryAspect.Fody.InstructionBlockChainCreator.CreateAspectInstance(CustomAttribute aspect)
1>MSBUILD : error :    at MethodBoundaryAspect.Fody.MethodWeaver.Weave(MethodDefinition method, CustomAttribute aspect, AspectMethods overriddenAspectMethods, ModuleDefinition moduleDefinition)
1>MSBUILD : error :    at MethodBoundaryAspect.Fody.ModuleWeaver.Execute(ModuleDefinition module)
1>MSBUILD : error :    at lambda_method(Closure , Object )
1>MSBUILD : error :    at InnerWeaver.ExecuteWeavers() in c:\ConsoleBuildAgent\work\ed448661dbb30d2e\FodyIsolated\InnerWeaver.cs:line 164
1>MSBUILD : error :    at InnerWeaver.Execute() in c:\ConsoleBuildAgent\work\ed448661dbb30d2e\FodyIsolated\InnerWeaver.cs:line 82
1>MSBUILD : error : Source:
1>MSBUILD : error : MethodBoundaryAspect.Fody
1>MSBUILD : error : TargetSite:
1>MSBUILD : error : Mono.Cecil.Cil.Instruction LoadConstOnStack(Mono.Cecil.TypeReference, System.Object)
1>MSBUILD : error : 
1>    Fody:   Finished Fody 416ms.
========== Build: 0 succeeded, 1 failed, 1 up-to-date, 0 skipped ==========

If I clone the repo and try and reproduce the error from within the repos unit tests, I can not, everything works fine.

Here is a repo that reproduces the issue

https://github.com/swestner/MethodBoundaryAspect.Test

Any ideas? I am happy to help, but I would need some direction.

Weaving fails with aspect inheritance

I have a situation where I want to implement 2 aspects: one to log exceptions and other to log and swallow them. Initially I had implemented them this way

public class LogExceptionAndThrowAttribute : OnMethodBoundaryAspect
{
        public override void OnException(MethodExecutionArgs arg)
        {
             //Log exception
        }
}

public class LogExceptionAndSwallowAttribute : LogExceptionAndThrowAttribute
{
        public override void OnException(MethodExecutionArgs arg)
        {
             base.OnExcpetion(arg); //log exception using base method

            //Set arg.returnvalue and FlowBehavior
        }
}

The problem is that when I build the project, I get this error:

Exception: Sequence contains no matching element
at System.Linq.Enumerable.Single[TSource](IEnumerable1 source, Func2 predicate)
at ModuleWeaver.WeaveRealmObjectHelper(TypeDefinition realmObjectType)
at ModuleWeaver.Execute()
at lambda_method(Closure , Object )
at InnerWeaver.ExecuteWeavers()
at InnerWeaver.Execute()

I implemented both aspects separately and now it works. I wanted to know if that's the expected behavior or it's an unexpected error.

Thank you.

Wiki Namespaces wrong

The examples in the front page wiki use the namespace
using MethodBoundaryAspect.Fody;

but version 1.0.66 uses the namespace

using MethodBoundaryAspect.Fody.Attributes;

Mark Fody as development dependency

I am having a hard time to create a nuget package with only MethodBoundaryAspect.dll as a neccessary reference. It always pulls Fody with it.
I've seen, that MethodBoundaryAspect.Fody.nuspec has development dependency set to true. This is not true. MethodBoundaryAspect.dll is needed throughout the system. Only Fody and FodyCecil can be left aside. Can you please mark Fody and FodyCecil as development dependencies?

Right now, my nuget will have a reference on MethodBoundaryAspect package which itself has a reference on Fody.
This has the effect, that even the top level exe project will search for FodyWeavers and pull everything in it finds in the reference tree.

Crash on ref return

ref return parameters were added in C# 7.0. However, weaving a method with such a return type causes a runtime exception when the OnExit handler attempts to access the MethodExecutionArgs's ReturnValue property.

Managed Debugging Assistant 'FatalExecutionEngineError'
Message=Managed Debugging Assistant 'FatalExecutionEngineError' : 'The runtime has encountered a fatal error. The address of the error was at 0x5ff4e838, on thread 0x424c. The error code is 0xc0000005. This error may be a bug in the CLR or in the unsafe or non-verifiable portions of user code. Common sources of this bug include user marshaling errors for COM-interop or PInvoke, which may corrupt the stack.'

That can be reproduced with this simple example:

class Aspect : OnMethodBoundaryAspect
{
    public override void OnExit(MethodExecutionArgs arg)
    {
        Console.WriteLine("Return value was {0}", arg.ReturnValue);
    }
}

class Program
{
    static void Main(string[] args)
    {
        Test.DoThing();
    }
}
    
static class Test
{
    public static int m_field;
        
    [Aspect]
    public static ref int DoThing()
    {
        return ref m_field;
    }
}

In short, MBA does not properly convert the ref return type to a non-ref object before setting it as the ReturnValue property of the MethodExecutionArgs. I went to add a test for this so I could fix it, and I ran into a different but related issue. Specifically, PEVerify considers ref return types to be invalid, even the latest version of PEVerify from the .NET 4.7.2 sdk running against a simple assembly output by the compiler with no code weaver involved. As such, whenever I try to add a method to the unit test assembly that uses a ref return type, the test fails because PEVerify fails, even though the code actually runs just fine.

I was able to find no evidence of short-term plans by Microsoft to update the verifier to account for this new language feature, so the only way I can get it to work at the moment is by telling PEVerify to ignore this particular error. I have a fix ready to PR which would fix the issue above, but the only way I can get the tests to pass is by simply disabling that error. Should I submit it as is (with the error disabled) or do you want to try to find another way to handle these issues?

Simple project fails

I've prepared the simple dotnet-cli based project and it fails to build:

Here's the project itself...

https://github.com/damageboy/fodytest

And the error:

C:\temp\fodytest>dotnet build
Microsoft (R) Build Engine version 15.6.84.34536 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.

  Restoring packages for C:\temp\fodytest\fodytest.csproj...
  Installing Fody 3.0.3.
  Generating MSBuild file C:\temp\fodytest\obj\fodytest.csproj.nuget.g.props.
  Generating MSBuild file C:\temp\fodytest\obj\fodytest.csproj.nuget.g.targets.
  Restore completed in 2.37 sec for C:\temp\fodytest\fodytest.csproj.
    Fody: Fody (version 3.0.3.0) Executing
MSBUILD : error : Fody: An unhandled exception occurred: [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error : Exception: [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error : Failed to execute weaver C:\Users\dans\.nuget\packages\methodboundaryaspect.fody\1.0.12\MethodBoundaryAspect.Fody.dll [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error : Type: [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error : System.Exception [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error : StackTrace: [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error :    at InnerWeaver.ExecuteWeavers() in C:\projects\fody\FodyIsolated\InnerWeaver.cs:line 208 [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error :    at InnerWeaver.Execute() in C:\projects\fody\FodyIsolated\InnerWeaver.cs:line 104 [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error : Source: [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error : FodyIsolated [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error : TargetSite: [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error : Void ExecuteWeavers() [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error : Object reference not set to an instance of an object. [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error : Type: [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error : System.NullReferenceException [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error : StackTrace: [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error :    at MethodBoundaryAspect.Fody.ReferenceFinder.GetMethodReference(TypeReference typeReference, Func`2 predicate) [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error :    at MethodBoundaryAspect.Fody.InstructionBlockChainCreator.CreateMethodExecutionArgsInstance(NamedInstructionBlockChain argumentsArrayChain, TypeReference anyAspectTypeDefinition) [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error :    at MethodBoundaryAspect.Fody.MethodWeaver.Weave(ModuleDefinition module, MethodDefinition method, IEnumerable`1 aspectInfos) [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error :    at MethodBoundaryAspect.Fody.ModuleWeaver.WeaveMethod(ModuleDefinition module, MethodDefinition method, List`1 aspectInfos) [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error :    at MethodBoundaryAspect.Fody.ModuleWeaver.WeaveType(ModuleDefinition module, TypeDefinition type, Collection`1 assemblyMethodBoundaryAspects) [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error :    at MethodBoundaryAspect.Fody.ModuleWeaver.Execute(ModuleDefinition module) [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error :    at InnerWeaver.ExecuteWeavers() in C:\projects\fody\FodyIsolated\InnerWeaver.cs:line 204 [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error : Source: [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error : MethodBoundaryAspect.Fody [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error : TargetSite: [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error : Mono.Cecil.MethodReference GetMethodReference(Mono.Cecil.TypeReference, System.Func`2[Mono.Cecil.MethodDefinition,System.Boolean]) [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error :  [C:\temp\fodytest\fodytest.csproj]
    Fody:   Finished Fody 335ms.

Build FAILED.

MSBUILD : error : Fody: An unhandled exception occurred: [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error : Exception: [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error : Failed to execute weaver C:\Users\dans\.nuget\packages\methodboundaryaspect.fody\1.0.12\MethodBoundaryAspect.Fody.dll [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error : Type: [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error : System.Exception [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error : StackTrace: [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error :    at InnerWeaver.ExecuteWeavers() in C:\projects\fody\FodyIsolated\InnerWeaver.cs:line 208 [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error :    at InnerWeaver.Execute() in C:\projects\fody\FodyIsolated\InnerWeaver.cs:line 104 [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error : Source: [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error : FodyIsolated [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error : TargetSite: [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error : Void ExecuteWeavers() [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error : Object reference not set to an instance of an object. [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error : Type: [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error : System.NullReferenceException [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error : StackTrace: [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error :    at MethodBoundaryAspect.Fody.ReferenceFinder.GetMethodReference(TypeReference typeReference, Func`2 predicate) [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error :    at MethodBoundaryAspect.Fody.InstructionBlockChainCreator.CreateMethodExecutionArgsInstance(NamedInstructionBlockChain argumentsArrayChain, TypeReference anyAspectTypeDefinition) [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error :    at MethodBoundaryAspect.Fody.MethodWeaver.Weave(ModuleDefinition module, MethodDefinition method, IEnumerable`1 aspectInfos) [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error :    at MethodBoundaryAspect.Fody.ModuleWeaver.WeaveMethod(ModuleDefinition module, MethodDefinition method, List`1 aspectInfos) [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error :    at MethodBoundaryAspect.Fody.ModuleWeaver.WeaveType(ModuleDefinition module, TypeDefinition type, Collection`1 assemblyMethodBoundaryAspects) [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error :    at MethodBoundaryAspect.Fody.ModuleWeaver.Execute(ModuleDefinition module) [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error :    at InnerWeaver.ExecuteWeavers() in C:\projects\fody\FodyIsolated\InnerWeaver.cs:line 204 [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error : Source: [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error : MethodBoundaryAspect.Fody [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error : TargetSite: [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error : Mono.Cecil.MethodReference GetMethodReference(Mono.Cecil.TypeReference, System.Func`2[Mono.Cecil.MethodDefinition,System.Boolean]) [C:\temp\fodytest\fodytest.csproj]
MSBUILD : error :  [C:\temp\fodytest\fodytest.csproj]
    0 Warning(s)
    1 Error(s)

Time Elapsed 00:00:04.35

Support for modification of ref parameters in the "OnEntry" override

It would be nice if ref parameters to a method could be changed as supported in the PostSharp On­Method­Boundary­Aspect

A screenshot of the relevant section from the link above is as seen below
image

Basically the functionality that I am looking for is as follows:

Every method that has a User type will be decorated with a DefaultUserAspect. This aspect will have an OnEntry override that searches through the argument list for a argument of type User. If the User parameter is found to be null, then the parameter will be replaced with the default user.

I have created a VS2017 project with the test cases helps indicate the behaviour that I am looking for in this extension. Please find it as attached below
MethodBoundaryAspect.Fody.Issue.Repro.zip

I would appreciate any feedback on any alternative or hint to implements the functionality that I am looking for. Thanks in advance!

On a side note: I am intrigued that the MethodInterceptionAspect has not yet had a similar implementation in Fody. Any thoughts on this would be welcome :)

Support net45

Would it be possible to add net45 to the supported frameworks?
Currently only net461 is set, this would prevent using this package in my environment.
(while supporting netstandard2.0 is great ;-D )

The weaver needs to update to at least version 3.0 of FodyHelpers

Following exception occurred after the update on Fody 3.0.1:

Fody: The weaver assembly 'MethodBoundaryAspect.Fody, Version=1.0.7.0, Culture=neutral, PublicKeyToken=296379d5065c9d61' references an out of date version of Mono.Cecil.dll. Expected strong name token of '1C-A0-91-87-7D-12-CA-03' but got '50-CE-BF-1C-CE-B9-D0-5E'. The weaver needs to update to at least version 3.0 of FodyHelpers.

Exception thrown if single aspect type has multiple constructor overloads of same length

Create an aspect that has multiple constructor overloads of the same length. Use one of those overloads, and an exception will be thrown during weaving.

I have a fix ready for it that I could submit a PR for, but there's no "contributing" file, so I don't know whether you would accept a PR from just anyone or what your standards are.

If you want to see what I did so you can review it, here is the branch.

My PR also fixes type arguments not being supported in the aspect ctor, array types not being supported in the aspect ctor, and named field assignments in aspect creation.

Watch variables while debugging an assembly. MethodName has "$_executor" prepended

When debugging MethodBoundaryAspect weaved code I can't add variables to Visual Studio's Watch, but rather have to look at 'Locals' where the names anonymized to __var_0, __var_1, etc.
Is there anyway around this, to get the actual variables names or to use Visual Studio's watch and hover over variable value display in debug mode?

I'm using MethodBoundaryAspect Exception catching. MethodBase.GetCurrentMethod().Name for a method called 'TestA' now returns "$_executor_TestA" instead of just 'TestA'. Is there a way to change this? Perhaps the aggressive inlining didn't inline it?

If it helps, in terms of weaving settings of some sort, here is an example of what the weaved code I get looks like.

WEAVEDCODE
public class ListBasedHolidayBusinessLogic : IHolidayBusinessLogic
{
public List Holidays;

public ListBasedHolidayBusinessLogic(List<LocalDate> holidays)
{
  this.Holidays = holidays;
}

[DebuggerStepThrough]
public bool IsHoliday(string countryCode, string cityCode, LocalDate testDate)
{
  object[] objArray = new object[3]
  {
    (object) countryCode,
    (object) cityCode,
    (object) testDate
  };
  MethodExecutionArgs methodExecutionArgs = new MethodExecutionArgs();
  methodExecutionArgs.set_Arguments(objArray);
  MethodBase currentMethod = MethodBase.GetCurrentMethod();
  methodExecutionArgs.set_Method(currentMethod);
  ListBasedHolidayBusinessLogic holidayBusinessLogic1 = this;
  methodExecutionArgs.set_Instance((object) holidayBusinessLogic1);
  ExceptionTrackerAspect exceptionTrackerAspect = new ExceptionTrackerAspect();
  ListBasedHolidayBusinessLogic holidayBusinessLogic2 = this;
  try
  {
    return holidayBusinessLogic2.\u0024_executor_IsHoliday(countryCode, cityCode, testDate);
  }
  catch (Exception ex)
  {
    Exception exception = ex;
    methodExecutionArgs.set_Exception(exception);
    exceptionTrackerAspect.OnException(methodExecutionArgs);
    if (methodExecutionArgs.get_FlowBehavior() != 1)
      throw ex;
    return (bool) methodExecutionArgs.get_ReturnValue();
  }
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool \u0024_executor_IsHoliday(string countryCode, string cityCode, LocalDate testDate)
{
  return this.Holidays != null && this.Holidays.Contains(testDate);
}

}

"Common Language Runtime detected an invalid program" with optimized code compilation

The verification of the following method fails, when it is compiled with the 'optimize code' compilation flag:

[DisplayWaitCursor]
private void TabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (_preventTabPageChange)
    {
        _preventTabPageChange = false;
        return;
    }

    if (e.OriginalSource.GetType() != typeof(TabControl) || Tabs.SelectedItem == null)
        return;

    var removedItem = e.RemovedItems.Count > 0 ? e.RemovedItems[0] as TabItem : null;

    if (removedItem?.Content is IEntityDialogPage && (!ValidateTabItemBySelect(removedItem) || !CommitTabItem(removedItem)))
    {
        e.Handled = true;
        _preventTabPageChange = true;
        Tabs.SelectedItem = removedItem;
    }
    else
    {
        _preventTabPageChange = false;
        InitializeSelectedDialogPage();
    }
}

It works, when the if part of the last if-statement is extracted into a separate method:

[DisplayWaitCursor]
private void TabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (_preventTabPageChange)
    {
        _preventTabPageChange = false;
        return;
    }

    if (e.OriginalSource.GetType() != typeof(TabControl) || Tabs.SelectedItem == null)
        return;

    var removedItem = e.RemovedItems.Count > 0 ? e.RemovedItems[0] as TabItem : null;

    if (removedItem?.Content is IEntityDialogPage && (!ValidateTabItemBySelect(removedItem) || !CommitTabItem(removedItem)))
    {
        IfPart(e, removedItem);
    }
    else
    {
        _preventTabPageChange = false;
        InitializeSelectedDialogPage();
    }
}

private void IfPart(SelectionChangedEventArgs e, TabItem removedItem)
{
    e.Handled = true;
    _preventTabPageChange = true;
    Tabs.SelectedItem = removedItem;
}

PE verification error:
Vescon.Biquanda.Client.Presentation.Toolkit.MasterData.AbstractDialog::TabControl_SelectionChanged][offset 0x00000095] jmp / exception into the middle of an instruction.(Error: 0x80131847)

A method body, which only contains an exception leads to a weaving error

When il code is weaved into the following method:

[Log]
public object Get(object inputParam)
{
  throw new NotImplementedException();
}

This exception occures:

Fody: PEVerify of the assembly failed.

Microsoft (R) .NET Framework PE Verifier.  Version  4.0.30319.33440
Copyright (c) Microsoft Corporation.  All rights reserved.

[IL]: Error: [D:\Builds\1\Product\MyAssembly.dll : NameSpace.To.My.Weaved.Class:Get][offset 0x00000060] Stack underflow.(Error: 0x80131857)
1 Error(s) Verifying D:\Builds\1\Product\MyAssembly.dll

PE verification error

The verification of the following method fails, when it is compiled with the 'optimize code' compilation flag:
See the comment in the code sample.

[DisplayWaitCursor]
private void Print_Click(object sender, RoutedEventArgs e)
{
    if (!FromDatePicker.SelectedDate.HasValue || !ToDatePicker.SelectedDate.HasValue)
        return;

    _ganttChartViewModel.VisibleRange = new DateSpan(
        FromDatePicker.SelectedDate.Value,
        ToDatePicker.SelectedDate.Value);

    SomeMethod();  // When the comment is removed it still fails

    var window = new ContainerWindow
                    {
                        Child = new PrintPreviewDialog { Document = CreatePrintDocument() },
                        Title = LangResources._Preview
                    };

    window.Show();
}

private void SomeMethod()
{
     // this method is really empty
}

PE verification error:
Vescon.Biquanda.Client.Presentation.Toolkit.MasterData.AbstractDialog::TabControl_SelectionChanged][offset 0x00000095] jmp / exception into the middle of an instruction.(Error: 0x80131847)

Weaving Exception on Async WinForm Event handlers

I am wanting to use this addin to support automatic logging using my logging framework. So far it works great except for this edge case.

Async WinForm event handlers have the signature of private async void SomeEvent( object sender )

This is an odd signature, notice they don't return Task, and I assume this is being treated as invalid.

These methods throw an InvalidOperationException during build as follows

Error Fody: An unhandled exception occurred:
Exception:
Failed to execute weaver F:\repositories\fabrica-script\packages\MethodBoundaryAspect.Fody.2.0.118\build..\weaver\MethodBoundaryAspect.Fody.dll
Type:
System.Exception
StackTrace:
at InnerWeaver.ExecuteWeavers() in C:\projects\fody\FodyIsolated\InnerWeaver.cs:line 206
at InnerWeaver.Execute() in C:\projects\fody\FodyIsolated\InnerWeaver.cs:line 111
Source:
FodyIsolated
TargetSite:
Void ExecuteWeavers()
Async state machine for System.Void Fabrica.Script.MainView::WhenRunCommandClicked(System.Object,DevExpress.XtraBars.ItemClickEventArgs) did not catch exceptions in the expected way.
Type:
System.InvalidOperationException
StackTrace:
at MethodBoundaryAspect.Fody.AsyncMethodWeaver.WeaveOnException(IList1 allAspects, Instruction instructionCallStart, Instruction instructionCallEnd, Instruction instructionAfterCall, IPersistable returnValue) at MethodBoundaryAspect.Fody.MethodWeaver.Weave() at MethodBoundaryAspect.Fody.ModuleWeaver.WeaveMethod(ModuleDefinition module, MethodDefinition method, List1 aspectInfos)
at MethodBoundaryAspect.Fody.ModuleWeaver.WeaveType(ModuleDefinition module, TypeDefinition type, Collection1 assemblyMethodBoundaryAspects) at MethodBoundaryAspect.Fody.ModuleWeaver.WeaveTypeAndNestedTypes(ModuleDefinition module, TypeDefinition type, Collection1 assemblyMethodBoundaryAspects)
at MethodBoundaryAspect.Fody.ModuleWeaver.Execute(ModuleDefinition module)
at MethodBoundaryAspect.Fody.ModuleWeaver.Execute()
at InnerWeaver.ExecuteWeavers() in C:\projects\fody\FodyIsolated\InnerWeaver.cs:line 178
Source:
MethodBoundaryAspect.Fody
TargetSite:
Void WeaveOnException(System.Collections.Generic.IList`1[MethodBoundaryAspect.Fody.AspectData], Mono.Cecil.Cil.Instruction, Mono.Cecil.Cil.Instruction, Mono.Cecil.Cil.Instruction, MethodBoundaryAspect.Fody.IPersistable) Fabrica.Script

MSBuild Error when using "dotnet build" or "msbuild"

The solution builds fine when using Visual Studio 2017, but fails when using the dotnet build or msbuild command.
The solution consists of 3 projects: MainProject, MainProject.UnitTests and MainProject.IntegrationTests.
Building just the main projects succeeds without any errors even when using cli commands, but UnitTests and IntegrationTests fail. Both Tests are referencing the MainProject in Visual Studio.

Suppressing Rethrow

Under certain circumstances it would be nice to not rethrow the exception. Without changing the code too much, just adding an if statement like so would be great:

        object[] objArray = new object[0];
        MethodExecutionArgs args = new MethodExecutionArgs();
        args.Arguments = objArray;
        MethodBase currentMethod = MethodBase.GetCurrentMethod();
        args.Method = currentMethod;
        ExceptionAspect exceptionAspect = new ExceptionAspect();
        exceptionAspect.OnEntry(args);
        try
        {
            throw (Exception)new NullReferenceException("Test");
        }
        catch (Exception ex)
        {
            args.Exception = ex;
            exceptionAspect.OnException(args);
            if (args != null)
                throw;
        }

Compiling with code optimization throws NullReferenceException

This method throws a NullReferenceException (someObject is never null), when it is compiled with code optimization (default Release configuration).

enum SomeEnum { Test1, Test2 };
abstract class SomeBaseObject { }
class SomeObject1 : SomeBaseObject { }
class SomeObject2 : SomeBaseObject { }

private static SomeEnum SomeMethod(SomeBaseObject someObject)
{
    if (someObject is SomeObject1)
        return SomeEnum.Test1;

    if (someObject is SomeObject2)
        return SomeEnum.Test2;

    throw new InvalidOperationException();
}

Can MBA.Fody be applied on only public method boundaries?

I want to implement a certain aspect that will only be applied on public APIs and not on private/protected/internal APIs...

Is this easily possible to do with MBA.Fody?

I would prefer a solution where the weaver can be somehow filtered to not even re-write the non public methods...

MethodBoundaryAspect fails if assembly has a delegate

MethodBoundaryAspect fails to weave some assemblies if the assembly has a delegate. A global assembly attribute is used to weave everything in an assembly. It fails saying the method has no body (meaning the delegate).
Here is a simple example.
https://github.com/JamesFlames/FailsToWeaveIfAssemblyHasDelegate/tree/master/FailsToWeave
I've added a pull request #55 that seems to fix this and also two tests one with an assembly which weaves and one with an assembly which fails to weave without the fix.

Global Aspect weaves it's own OnEntry method, ends up with a StackOverflowException

Hello, I have an simple OnMethodBoundaryAspect that is supposed to do some logging for me.
I apply it to the assembly, so it get's added to every class and method in the assembly.
But my aspect is in that assembly too. So it will be added to it's own OnEntry method, which will lead to a StackOverflowException.

public class MyAspect : OnMethodBoundaryAspect
{
    public override void OnEntry(MethodExecutionArgs args)
    {
        Console.WriteLine("OnEntry");
    }
}

In AssemblyInfo.cs

[assembly: MyAspect]

image

Add support for linked weaver files

When using a linked weaver file (FodyWeaver.xml), this isn't recognised at buildtime (with the error Fody: Could not find path to weavers file).
The files is added as a link with the following code:
<ItemGroup> <Content Include="..\..\..\Resources\Config\FodyWeavers.xml"> <Link>FodyWeavers.xml</Link> </Content> </ItemGroup>

This would be easier to use since all the weaver files will contain the same extensions (for now).

Cannot debug weaved method

The problem experienced after the reimplemented weaver changes

I used skipped test "IfWeavedTestProgramIsExecuted_ThenTheDebugSymbolsShouldWorkAndTheDebuggerShouldBeAttachable" for checking up: breakpoint into TestClass.DoIt is never hitting.

Update Readme to indicate installation instructions

It's unclear whether any or which assemblies require to be installed on the customer's computer that has an assembly that uses this technology. The Readme should indicate this information, or refer to documentation which does.

Sign assembly with a strong name

Please add a strong name key and sign the MethodBoundaryAspect.Fody assembly.

This is useful for projects that require any referenced assemblies to be signed, e.g. Microsoft SharePoint or CRM.

Structs are unweaveable

A weaved struct seems to throw exceptions.

using static System.Console;

class Aspect : OnMethodBoundaryAspect
{
    public override void OnEntry(MethodExecutionArgs arg) => WriteLine("Arg was {0}", arg.Arguments[0]);
}

class Program
{
    static void Main(string[] args) => new Struct().DoSomething("Value");
}

public struct Struct
{
    [Aspect] public void DoSomething(string s) => WriteLine("Something: {0}", s);
}

I'll submit a PR in the next few days.

Generic Types: Method not fully instantiated

Maybe this follows on #12

I have following .net core class

public class GenericHibernateDao<TEntity, TId> : IGenericHibernateDao<TEntity, TId> where TEntity : Entity{
    [FodyDummy]
    public IList<TEntity> GetAll() {
        return CurrentSession.CreateCriteria(typeof(TEntity)).List<TEntity>();
    }
}

Error that I get during execution/test run (not on compile):

System.InvalidOperationException : Could not execute the method because either the method itself or the containing type is not fully instantiated.

The decomlied source looks like this:

[DebuggerStepThrough]
[Transaction(true, new Type[] {})]
public IList<TEntity> GetAll()
{
    object[] objArray = new object[0];
    MethodExecutionArgs args = new MethodExecutionArgs();
    args.set_Arguments(objArray);
    MethodBase currentMethod = MethodBase.GetCurrentMethod();
    args.set_Method(currentMethod);
    object obj = (object) this;
    args.set_Instance(obj);
    TransactionAttribute transactionAttribute = new TransactionAttribute(true, new Type[0]);
    transactionAttribute.OnEntry(args);
    GenericHibernateDao<TEntity, TId> genericHibernateDao = this;
    IList<TEntity> all;
    try
    {
        all = ((GenericHibernateDao<,>) genericHibernateDao).$_executor_GetAll();
    }
    catch (Exception ex)
    {
        Exception exception = ex;
        args.set_Exception(exception);
        transactionAttribute.OnException(args);
        throw ex;
    }
    args.set_ReturnValue((object) all);
    transactionAttribute.OnExit(args);
    return all;
}

What I think that could trigger the Exception is the line all = ((GenericHibernateDao<,>) genericHibernateDao).$_executor_GetAll(); where the generic types are missing. Two lines above they are correctly set GenericHibernateDao<TEntity, TId> genericHibernateDao = this;

Would be nice if you could have a look at this ;-)
Thanks,
bergziege

Can you get the return type of a method?

I'm trying to change the ReturnValue in OnExit for a method. I'm getting the value from a JSON file, so I need to cast it before I can set it as the ReturnValue. Is it possible to get the return type of the method?

For example: arg.ReturnValue =(arg.Method.ReturnType).jsonFile["newValue"]

Is there a reason DisableWeaver can only applied to methods and not classes?

I am using this addin to implement automatic logging with my logging framework. When adding the automatic logging at first the attribute is applied on the assembly(ies). As more detailed manual logging is added to a particular class, I would like to mark that class with DisableWeaver. Is there anything that prevents DisableWeaver from being applied to a class other then its AttributeUsage does not allow it? If Class was added to the AttributeUsage should it work?

Thanks
Jim

Issue when using 1.0.12: common language runtime detected an invalid program

No idea if this is MethodBoundryAspect or the Fody specific, but when using the latest MethoudBoundry (with Fody 3.0.3 and Fody.Cecil 3.0.1) I get the following exception with some of the generated code (not all):

Common language runtime detected an invalid program

The code for the file that gives this exception is the folowing
MethodBoundaryAspect.Fody 1.0.3

using EESSI.Foundation.Cache.Core.Interfaces;
using EESSI.Foundation.Core;
using EESSI.Foundation.Core.Guards;
using EESSI.Foundation.Core.Logging;
using MethodBoundaryAspect.Fody.Attributes;
using System;
using System.Reflection;

namespace EESSI.Foundation.Data.Repositories
{
  public abstract class HybridRepository
  {
    protected readonly ICacheClient CacheClient;

    protected HybridRepository(ICacheClient cacheClient)
    {
      this.CacheClient = cacheClient;
    }

    [LogStartStop]
    protected Maybe<TResult> GetFromCacheOrDatabase<TResult>(ICacheKey cacheKey, Func<Maybe<TResult>> databaseOperation)
    {
      object[] objArray = new object[2]
      {
        (object) cacheKey,
        (object) databaseOperation
      };
      MethodExecutionArgs args = new MethodExecutionArgs();
      args.Arguments = objArray;
      MethodBase currentMethod = MethodBase.GetCurrentMethod();
      args.Method = currentMethod;
      object obj = (object) this;
      args.Instance = obj;
      LogStartStopAttribute startStopAttribute = new LogStartStopAttribute();
      startStopAttribute.OnEntry(args);
      Guard.NotNull<ICacheKey>(cacheKey, nameof (cacheKey));
      Guard.NotNull<Func<Maybe<TResult>>>(databaseOperation, nameof (databaseOperation));
      Maybe<TResult> maybe1 = this.CacheClient.Read<TResult>(cacheKey);
      if (!maybe1.IsPresent)
      {
        maybe1 = databaseOperation();
        if (maybe1.IsPresent)
          this.CacheClient.Write<TResult>(cacheKey, maybe1.Value);
      }
      Maybe<TResult> maybe2 = maybe1;
      args.ReturnValue = (object) maybe2;
      startStopAttribute.OnExit(args);
      return maybe2;
    }
  }
}

MethodBoundaryAspect.Fody 1.0.12

using EESSI.Foundation.Cache.Core.Interfaces;
using EESSI.Foundation.Core;
using EESSI.Foundation.Core.Guards;
using EESSI.Foundation.Core.Logging;
using MethodBoundaryAspect.Fody.Attributes;
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace EESSI.Foundation.Data.Repositories
{
    public abstract class HybridRepositoryTest
    {
        protected readonly ICacheClient CacheClient;

        protected HybridRepositoryTest(ICacheClient cacheClient)
        {
            this.CacheClient = cacheClient;
        }

        [LogStartStop]
        [DebuggerStepThrough]
        protected Maybe<TResult> GetFromCacheOrDatabase<TResult>(ICacheKey cacheKey, Func<Maybe<TResult>> databaseOperation)
        {
            object[] objArray = new object[2]
            {
        (object) cacheKey,
        (object) databaseOperation
            };
            MethodExecutionArgs args = new MethodExecutionArgs();
            args.Arguments = objArray;
            MethodBase currentMethod = MethodBase.GetCurrentMethod();
            args.Method = currentMethod;
            object obj = (object)this;
            args.Instance = obj;
            LogStartStopAttribute startStopAttribute = new LogStartStopAttribute();
            startStopAttribute.OnEntry(args);
            Maybe<TResult> fromCacheOrDatabase = this.\u0024_executor_GetFromCacheOrDatabase(cacheKey, databaseOperation);
            args.ReturnValue = (object)fromCacheOrDatabase;
            startStopAttribute.OnExit(args);
            return fromCacheOrDatabase;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private Maybe<TResult> \u0024_executor_GetFromCacheOrDatabase<TResult>(ICacheKey cacheKey, Func<Maybe<TResult>> databaseOperation)
    {
      Guard.NotNull<ICacheKey>(cacheKey, nameof(cacheKey));
      Guard.NotNull<Func<Maybe<TResult>>>(databaseOperation, nameof(databaseOperation));
      Maybe<TResult> maybe = this.CacheClient.Read<TResult>(cacheKey);
      if (!maybe.IsPresent)
      {
        maybe = databaseOperation();
        if (maybe.IsPresent)
          this.CacheClient.Write<TResult>(cacheKey, maybe.Value);
      }
      return maybe;
    }
  }
}

I couldn't find out if this was Fody specific or related to your plugin. But I tried with Fody 3.0.1 as well and still got this exception.
Sorry if this is a Fody problem, the you can ignore this :)

Simple Example for "MethodBoundaryAspect.Fody"

Hello

I try to implement the "MethodBoundaryAspect.Fody" but i cannot access to the abstract class "OnMethodBoundaryAspect". I don´t know why!
I think i initialize the "MethodBoundaryAspect.Fody" wrong :/

Is it possible to insert a MethodBoundaryAspect-Sample into this folder?
Would be nice! :)

In the OnException method get the value from the body of the main method?

Hi, developers!

Tell me, please, is there a possibility in the OnException method to get the value from the body of the main method?

For example, I perform some method, the state of my objects in the method changes, and there is an exception. I need to log the state of my objects at the time of the error.

Build fails when using Fody 6.0 and adding attribute to method invoking static method

This works on 5.2.0 but fails on 6.0

The code is pretty simple, just calling a static method on a 3rd party library (SpreadsheetGear)

[MyAttribute]
private IWorkbook GetWorkbookFromMemory(byte[] buffer)
{
    return Factory.GetWorkbookSet().Workbooks.OpenFromMemory(buffer);
}
7>MSBUILD : warning FodyVersionMismatch: Fody: Weavers should reference at least the current major version of Fody (version 6). The weaver in MethodBoundaryAspect.Fody references version 3. This may result in incompatibilities at build time such as MissingMethodException being thrown.
7>MSBUILD : error : Fody: Failed to execute weaver C:\Users\silvro\.nuget\packages\methodboundaryaspect.fody\2.0.113\build\..\netclassicweaver\MethodBoundaryAspect.Fody.dll due to a MissingMemberException. Message: Method not found: 'Mono.Collections.Generic.Collection`1<Mono.Cecil.TypeReference> Mono.Cecil.GenericParameter.get_Constraints()'.. This is likely due to the weaver referencing an old version (3) of Fody.

Not that I need or even know what the features are but thought I'd raise it.
Does it just need updated to 6.0?

type argument 'T' violates the constraint of type parameter 'T'.

With the latest release I have the following exception in the most basic application:

type argument 'T' violates the constraint of type parameter 'T'.

Testproject where this fails can be found at https://github.com/Wautvda/TestProjectMethodBoundryAspect
The only thing it does is serialize a class to XML and the serialisation methods are intercepted by the MethodBoundryAspect and print some testcode to console.

If the Attribute is removed everything runs fine. Haven't found the real cause yet.

Fody v3.03
MethodBoundryAspect v1.0.41

CS0103 while debugging on .NET Core 2.2

Hello,

After I upgraded my Web Api project from .Net Core 2.1 to 2.2, I can no longer see variables when debugging. I am getting "CS0103 : variable not defined in current context" errors. Although intellisense recognizes and autocompletes variable names in Immediate Window, it says variable not defined there either. Removing the attribute fixes the problem.

Note that this problem is caused when my attribute has classes as target. I have another attribute targeting methods and it does not create issues for me.

You can find a minimal project with issue here :
https://github.com/firatakyildiz/MethodBoundaryAspectBug

You can put a brekapoint at Domain.Operation.SampleMethod and see the issue.

Thanks in advance

unhandled exception with ctor params

I try to add params to the ctor, like MyAspect(params string[] vals)

but this results in:

Fody: Fody (version 1.0.0.0) Executing
MSBUILD : error : Fody: An unhandled exception occurred:
MSBUILD : error : Exception:
MSBUILD : error : Parametertype: System.String[]
 StackTrace:
    at MethodBoundaryAspect.Fody.InstructionBlockCreator.LoadValueOnStack(TypeReference parameterType, Object value, ModuleDefinition module)
    at MethodBoundaryAspect.Fody.InstructionBlockCreator.NewObject(VariableDefinition newInstance, TypeReference instanceTypeReference, ModuleDefinition module, CustomAttribute aspect, Int32 aspectCounter)
    at MethodBoundaryAspect.Fody.InstructionBlockChainCreator.CreateAspectInstance(CustomAttribute aspect)
    at MethodBoundaryAspect.Fody.MethodWeaver.Weave(MethodDefinition method, CustomAttribute aspect, AspectMethods overriddenAspectMethods, ModuleDefinition moduleDefinition)

I also tried to leave the ctor alone and to it like this, with a property in the attribute:

[MyAspect(Foo = new[] { nameof(Bar), nameof(Baz) })]

but this throws the same unhandled exception.

MethodBoundaryAspect library necessarily depends on NetStandard.Library

Importing MethodBoundaryAspect forces the library that it is imported in to depend on NetStandard.Library. Also, it only allows for .NET 4.5.2 or above and .NET Standard 2.0.

If the NetStandard.Library package is not imported, then the following error occurs:

Error CS0012 The type 'Attribute' is defined in an assembly that is not referenced. You must add a reference to assembly 'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'.

Such a limitation could easily be fixed in the library by explicitly stating its dependencies.

Since Fody itself can use PrivateAssets="all", it would be trivial to offer MethodBoundaryAspect as a completely separate NuGet package which will support any framework version and standard, as there is no reason for a .NET Standard 2.0 limitation. Such a package would, then, be used by MethodBoundaryAspect.Fody, which is the actual weaver.

With this, the weaver is completely disconnected from the metadata package, and two problems are solved:

  1. All possible targets are supported
  2. The runtime dependency issue on the weaver package itself is fixed

Generic type support

I have following class

public class SampleClass<T>
{
        [Log]
        public void Test()
        {
            Console.WriteLine("Method Body");
        }
}

Error that I get during the compilation

Fody: PEVerify of the assembly failed.
[IL]: Error: [SampleClass`1[T]::Test] Type load failed.(Error: 0x801318F3)

Here is decompiled class

 public class SampleClass<T>
  {
    [Log]
    public void Test()
    {
      object[] objArray = new object[0];
      MethodExecutionArgs args = new MethodExecutionArgs();
      args.Arguments = objArray;
      MethodBase currentMethod = MethodBase.GetCurrentMethod();
      args.Method = currentMethod;
      SampleClass<> sampleClass = (SampleClass<>) this;
      args.Instance = (object) sampleClass;
      LogAttribute logAttribute = new LogAttribute();
      logAttribute.OnEntry(args);
      try
      {
        Console.WriteLine("Method Body");
      }
      catch (Exception ex)
      {
        args.Exception = ex;
        logAttribute.OnException(args);
        throw;
      }
      logAttribute.OnExit(args);
    }
  }

There's no generic parameter in the following line, and that is the reason of the problem.

SampleClass<> sampleClass = (SampleClass<>) this;

Possible solution:

I tried to use object type instead of declaring type for testing

InstructionBlockChainCreator >> CreateMethodExecutionArgsInstance

public NamedInstructionBlockChain CreateMethodExecutionArgsInstance(NamedInstructionBlockChain argumentsArrayChain)
{
       // instance value
       var instanceVariable = _creator.CreateVariable(_method.DeclaringType);
       var createThisVariableBlock = _creator.CreateThisVariable(instanceVariable, _method.DeclaringType);
       ...
}
public NamedInstructionBlockChain CreateMethodExecutionArgsInstance(NamedInstructionBlockChain argumentsArrayChain)
{
       var objectType = _referenceFinder.GetTypeReference(typeof(object));
       var instanceVariable = _creator.CreateVariable(objectType);
       var createThisVariableBlock = _creator.CreateThisVariable(instanceVariable, objectType);
        ...
}

Result

  public class SampleClass<T>
  {
    [Log]
    public void Test()
    {
      object[] objArray = new object[0];
      MethodExecutionArgs args = new MethodExecutionArgs();
      args.Arguments = objArray;
      MethodBase currentMethod = MethodBase.GetCurrentMethod();
      args.Method = currentMethod;
      object obj = (object) this;
      args.Instance = obj;
      LogAttribute logAttribute = new LogAttribute();
      logAttribute.OnEntry(args);
      try
      {
        Console.WriteLine("Method Body");
      }
      catch (Exception ex)
      {
        args.Exception = ex;
        logAttribute.OnException(args);
        throw;
      }
      logAttribute.OnExit(args);
    }
  }
}

Integration with Polly to implement retry operations

Hello everyone!

I've made search in issues and other places like StackOverflow and Google and I could't find nothing about that.

I want to create a attribute to implemente resilient operations, using Polly, but I have a lot of doubts and I don't found way to make this implementation.

Looking to Postsharp Method Interception documentation, we can see a way to know when our method is invoked and intercept.

Exists some thing like that inside Fody MethodBoundary?

Using PostSharp we can make something like that:

public class RetryOnExceptionAttribute : MethodInterceptionAspect
{
     public RetryOnExceptionAttribute()
     {
        this.MaxRetries = 3;
     }

     public int MaxRetries { get; set; }

     public override void OnInvoke(MethodInterceptionArgs args)
     {
        Policy
            .Handle<SomeExceptionType>()
            .Retry(MaxRetries, onRetry: (exception, retryCount) =>
            {
                    // do something 
            }).Execute(args.Proceed);
        }
}

My apologies if I made mistakes

Apply Aspect globally in AssemblyInfo.cs

Is it still possible to apply Aspects globally at assembly level?
How is this done? Can wildcards be used to apply Aspects just to a namespace within an assembly?

MethodBoundaryAspect cannot be used in dotnet standard with default NuGet restore

The behaviour I am describing below is something that I have seen in both .net core 2.0 applications and .net standard 2.0 class libraries.

When installing the MethodBoundaryAspect.Fody package, from the UI or command line tools, the library is not immediately usable without manual package configuration. This is demonstrated below. I can not see any documentation regarding this anywhere, is this intended behaviour? Or does the 'fix' described below have any undesired side effect?

After installing the NuGet package the entry in .csproj will look as follows:

<PackageReference Include="MethodBoundaryAspect.Fody" Version="1.0.66">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>

With the package configured as follows, the library is unusable as it is not detected by the compiler - see screenshot below.

image

To resolve the issue I change the csproj package reference to the following and all works again.

<PackageReference Include="MethodBoundaryAspect.Fody" Version="1.0.66" />

Common Language Runtime detected an invalid program

Hi team, last week I have a bug on production site
This is a case that caused bug (I call it as "FAKE" code - short code to reproduce):

[TimeTakenTrackLog]
public class Demo
{
    public static async Task DoNothing()
    {

    }

    public static async Task DemoBugAsync(int value)
    {
        if (value > 0)
        {
            await DoNothing();
        }
    }
}

// My page
[TimeTakenTrackLog] // applied on class
protected void Page_Load(object sender, EventArgs e)
{
    Demo.DemoBugAsync(5).GetAwaiter().GetResult();
}

// My aspect attribute
public class TimeTakenTrackLogAttribute : OnMethodBoundaryAspect
{
    public const string MessageQueueKey = "ImMethodTimeTakenMsgQueue";

    public override void OnEntry(MethodExecutionArgs args)
    {
            
    }

    public override void OnException(MethodExecutionArgs args)
    {
           
    }

    public override void OnExit(MethodExecutionArgs args)
    {
        if (args.ReturnValue is Task t)
        {
            t.ContinueWith(task => {
                BuildTimeTakenData(EventType.OnExitAsync, args);
                EnqueueTrackMessage();
        });
        }
        else
        {
            BuildTimeTakenData(EventType.OnExit, args);
            EnqueueTrackMessage();
        }         
    }  
}

=> It's just a piece of "real" code I applied to production last week (but it can cause error)
=> Fake code cause error on both DEBUG and RELEASE, local site & production site
=> "Real" code worked on Local (Debug mode)/TEST (Release mode) site BUT DID NOT work on Production site (RELEASE mode)


Here is a mini version of real code:

Caller:

WriteDbAccess.InitOrderRequestAndParseSubTrackData(new OrderRequestItem
{
    OrderId = trackRequestData.OrderId,
    SupplierId = supplierId,
    OrderStatus = OrderStatus.New
}, NewPORequest, response).GetAwaiter().GetResult();

=> And here is method InitOrderRequestAndParseSubTrackData in class WriteDbAccess:
I used Dapper btw

public static async Task InitOrderRequestAndParseSubTrackData(OrderRequestItem dto, NewPORequest xmlRequest, NewPOResponse xmlResponse)
{
    using (var conn = new SqlConnection(Settings.ConnectionString))
    {
        conn.Open();

        var poIdNumber = xmlRequest?.PurchaseOrder?.purchaseOrderId;
        if (poIdNumber != null && poIdNumber > 0)
        {
            var exists = conn.ExecuteScalar<bool>("select top 1 1 from dbo.YOUR_TABLE where PurchaseOrderId = @poId", new { poId = poIdNumber.ToString() });

            if (!exists)
            {
                var parms = new DynamicParameters();

                parms.Add("@orderId", dto.OrderId);
                parms.Add("@supplierId", dto.SupplierId);
                parms.Add("@orderStatus", dto.OrderStatus);

                await conn.ExecuteAsync("InsertOrderRequest", parms,
                            commandType: CommandType.StoredProcedure, commandTimeout: Settings.ConnectionTimeout);

                await ParseOrderRequestSubTrackDataAsync(dto.OrderId, xmlRequest, xmlResponse);
            }
        }
    }
}

Remember that Real code work on local and test site, but it did not work on Production site :(

More images:

image

image

!!! FYI: My Aspect attribute work good, I have some web services and aspect worked for those services, only one of them did not work

This can be a bug of Fody , not MethodBoundaryAspect.Fody but I don't know
So anyone can explain why please

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.