DotNetAspects 1.4.1

dotnet add package DotNetAspects --version 1.4.1
                    
NuGet\Install-Package DotNetAspects -Version 1.4.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="DotNetAspects" Version="1.4.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="DotNetAspects" Version="1.4.1" />
                    
Directory.Packages.props
<PackageReference Include="DotNetAspects" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add DotNetAspects --version 1.4.1
                    
#r "nuget: DotNetAspects, 1.4.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package DotNetAspects@1.4.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=DotNetAspects&version=1.4.1
                    
Install as a Cake Addin
#tool nuget:?package=DotNetAspects&version=1.4.1
                    
Install as a Cake Tool

DotNetAspects

<div align="center">

A lightweight, high-performance AOP library for .NET

The free, open-source alternative to PostSharp

NuGet NuGet Downloads License .NET

Features | Quick Start | Performance | Migration from PostSharp | Documentation

</div>


Why DotNetAspects?

PostSharp DotNetAspects
Cost $499-$4,999/year per developer Free forever
License Proprietary MIT (Open Source)
Weaving Compile-time IL Compile-time IL (Fody)
API Compatibility - PostSharp-compatible
Performance Excellent Excellent

DotNetAspects provides the same powerful AOP capabilities as PostSharp with a compatible API, making migration straightforward while eliminating licensing costs.


Features

  • MethodInterceptionAspect - Full control over method execution with Proceed() support
  • OnMethodBoundaryAspect - Execute code at method entry, success, exception, and exit
  • LocationInterceptionAspect - Intercept property and field get/set operations
  • PostSharp-Compatible API - Minimal code changes for migration
  • Compile-Time IL Weaving - Zero runtime reflection overhead using Fody
  • Aspect Instance Caching - Optimized for high-throughput scenarios
  • Strong-Named Assembly - Enterprise-ready with PublicKeyToken 97f295f398ec39b7
  • Multi-Targeting - Supports netstandard2.0 and net8.0

Quick Start

1. Install the package

dotnet add package DotNetAspects
dotnet add package Fody

2. Create an aspect

using DotNetAspects.Interception;
using DotNetAspects.Args;

public class LoggingAspect : MethodInterceptionAspect
{
    public override void OnInvoke(MethodInterceptionArgs args)
    {
        Console.WriteLine($"Calling: {args.Method.Name}");

        args.Proceed();  // Execute the original method

        Console.WriteLine($"Result: {args.ReturnValue}");
    }
}

3. Apply to your code

public class OrderService
{
    [LoggingAspect]
    public Order ProcessOrder(int orderId, decimal amount)
    {
        // Your business logic here
        return new Order { Id = orderId, Total = amount };
    }
}

4. Build and run

dotnet build

That's it! The aspect is woven at compile time - no runtime configuration needed.


Performance

DotNetAspects v1.4.0 is optimized for high-throughput enterprise scenarios:

Benchmarks

Scenario Operations Time Throughput
Method Interception 10,000 27ms ~370,000 ops/sec
Concurrent Access (100 threads) 50,000 38ms ~1.3M ops/sec
Banking Transactions 3,000 <5ms ~600,000 ops/sec

Optimizations in v1.4.0

  • Aspect Instance Caching - Aspects are created once and reused (lazy singleton pattern)
  • Zero-Copy Arguments - GetRawArray() avoids unnecessary array allocations
  • Cached Method Binding - Reduces object allocation in hot paths
  • Minimal GC Pressure - Sustained high throughput without Gen2 collections

Running Benchmarks

cd tests/DotNetAspects.LoadTests
dotnet run -c Release

Installation

Package Reference

<ItemGroup>
  <PackageReference Include="DotNetAspects" Version="1.4.0" />
  <PackageReference Include="Fody" Version="6.8.2">
    <PrivateAssets>all</PrivateAssets>
    <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
  </PackageReference>
</ItemGroup>

CLI

dotnet add package DotNetAspects --version 1.4.0
dotnet add package Fody

Note: No FodyWeavers.xml is required - the weaver is configured automatically.


Usage Examples

Method Interception

Intercept any method call with full control:

public class CachingAspect : MethodInterceptionAspect
{
    private static readonly Dictionary<string, object> Cache = new();

    public override void OnInvoke(MethodInterceptionArgs args)
    {
        var key = $"{args.Method.Name}:{string.Join(",", args.Arguments)}";

        if (Cache.TryGetValue(key, out var cached))
        {
            args.ReturnValue = cached;
            return;  // Skip original method
        }

        args.Proceed();
        Cache[key] = args.ReturnValue;
    }
}

Method Boundary

Execute code at specific points in method execution:

public class TimingAspect : OnMethodBoundaryAspect
{
    public override void OnEntry(MethodExecutionArgs args)
    {
        args.Tag = Stopwatch.StartNew();
    }

    public override void OnSuccess(MethodExecutionArgs args)
    {
        Console.WriteLine($"{args.Method.Name} completed successfully");
    }

    public override void OnException(MethodExecutionArgs args)
    {
        Console.WriteLine($"{args.Method.Name} threw: {args.Exception.Message}");
    }

    public override void OnExit(MethodExecutionArgs args)
    {
        var sw = (Stopwatch)args.Tag;
        Console.WriteLine($"{args.Method.Name} took {sw.ElapsedMilliseconds}ms");
    }
}

Property Interception

Intercept property get/set operations:

public class NotifyChangedAspect : LocationInterceptionAspect
{
    public override void OnGetValue(LocationInterceptionArgs args)
    {
        args.ProceedGetValue();
        Console.WriteLine($"Get {args.LocationName}: {args.Value}");
    }

    public override void OnSetValue(LocationInterceptionArgs args)
    {
        var oldValue = args.GetCurrentValue();
        args.ProceedSetValue();

        if (!Equals(oldValue, args.Value))
        {
            Console.WriteLine($"{args.LocationName} changed: {oldValue} -> {args.Value}");
        }
    }
}

Aspect with Configuration

public class RetryAspect : MethodInterceptionAspect
{
    public int MaxRetries { get; set; } = 3;
    public int DelayMs { get; set; } = 1000;

    public override void OnInvoke(MethodInterceptionArgs args)
    {
        for (int i = 0; i <= MaxRetries; i++)
        {
            try
            {
                args.Proceed();
                return;
            }
            catch when (i < MaxRetries)
            {
                Thread.Sleep(DelayMs);
            }
        }
    }
}

// Usage
[RetryAspect(MaxRetries = 5, DelayMs = 500)]
public void UnreliableOperation() { }

Migration from PostSharp

DotNetAspects provides a PostSharp-compatible API for easy migration:

Namespace Changes

- using PostSharp.Aspects;
- using PostSharp.Serialization;
+ using DotNetAspects.Interception;
+ using DotNetAspects.Args;

Code Changes

- [PSerializable]  // Not required in DotNetAspects
  public class MyAspect : MethodInterceptionAspect
  {
      // No changes needed to the aspect logic!
  }

API Mapping

PostSharp DotNetAspects
PostSharp.Aspects.MethodInterceptionAspect DotNetAspects.Interception.MethodInterceptionAspect
PostSharp.Aspects.OnMethodBoundaryAspect DotNetAspects.Interception.OnMethodBoundaryAspect
PostSharp.Aspects.LocationInterceptionAspect DotNetAspects.Interception.LocationInterceptionAspect
PostSharp.Aspects.MethodInterceptionArgs DotNetAspects.Args.MethodInterceptionArgs
PostSharp.Aspects.MethodExecutionArgs DotNetAspects.Args.MethodExecutionArgs
[PSerializable] Not required

Detailed Guide: See MIGRATION_GUIDE.md for enterprise migration strategies.

Version History: See CHANGELOG.md for all releases.


Documentation

API Reference

MethodInterceptionArgs
Property/Method Description
Instance Object instance (null for static methods)
Method MethodBase of the intercepted method
Arguments Method arguments (IArguments)
ReturnValue Get/set the return value
Proceed() Execute original method with current arguments
Invoke(args) Execute original method with custom arguments
MethodExecutionArgs
Property/Method Description
Instance Object instance (null for static methods)
Method MethodBase of the executing method
Arguments Method arguments (IArguments)
ReturnValue Return value (in OnSuccess/OnExit)
Exception Exception thrown (in OnException)
Tag Pass state between aspect methods
FlowBehavior Control flow after aspect returns
LocationInterceptionArgs
Property/Method Description
Instance Object instance (null for static)
Location PropertyInfo/FieldInfo
LocationName Name of the property/field
Value Value being get/set
GetCurrentValue() Get current value from location
SetNewValue(value) Set value to location
ProceedGetValue() Proceed with get operation
ProceedSetValue() Proceed with set operation

Project Structure

src/
├── DotNetAspects/           # Core library
│   ├── Args/                # Argument classes
│   ├── Interception/        # Aspect base classes
│   └── Extensibility/       # Multicast attributes
└── DotNetAspects.Fody/      # IL weaver

tests/
├── DotNetAspects.Tests/     # Unit tests
└── DotNetAspects.LoadTests/ # Performance benchmarks

Roadmap

  • MethodInterceptionAspect with Proceed()
  • OnMethodBoundaryAspect
  • LocationInterceptionAspect
  • Constructor argument support
  • Aspect instance caching
  • Performance optimizations for high-throughput
  • Async method interception
  • Assembly-wide aspect application
  • Aspect multicasting with patterns

Contributing

Contributions are welcome! Here's how to get started:

  1. Fork the repository
  2. Clone your fork: git clone https://github.com/your-username/DotNetAspects.git
  3. Create a branch: git checkout -b feature/amazing-feature
  4. Make your changes and add tests
  5. Run tests: dotnet test
  6. Commit: git commit -m 'Add amazing feature'
  7. Push: git push origin feature/amazing-feature
  8. Open a Pull Request

Development Setup

git clone https://github.com/RlyehDoom/DotNetAspects.git
cd DotNetAspects
dotnet build
dotnet test

Running Benchmarks

cd tests/DotNetAspects.LoadTests
dotnet run -c Release

License

This project is licensed under the MIT License - see the LICENSE file for details.


Acknowledgments

  • Fody - The IL weaving framework that makes this possible
  • Mono.Cecil - The library used for IL manipulation
  • PostSharp - For pioneering .NET AOP and inspiring this compatible alternative

<div align="center">

Made with love for the .NET community

If you find this project useful, please consider giving it a star!

</div>

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

  • net8.0

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.4.1 120 12/13/2025
1.4.0 113 12/13/2025
1.3.6 124 12/12/2025
1.3.5 114 12/12/2025
1.3.4 117 12/12/2025
1.3.3 121 12/12/2025
1.3.2 440 12/10/2025
1.3.1 433 12/10/2025
1.3.0 429 12/10/2025
1.2.0 439 12/10/2025
1.1.0 432 12/10/2025
1.0.2 765 12/10/2025
1.0.0 429 12/10/2025