Description
AspectNet brings Aspect-Oriented Programming to .NET by using compile‑time IL weaving. It enables the creation of cross‑cutting behaviours and ASP.NET‑style middleware pipelines that can be applied to any class, method, constructor, or property—regardless of visibility or static/instance context. And best of all, it's free and open-source.
Features
Attribute‑driven aspect model
It defines aspects using custom attributes applied directly to classes, constructors, methods, or properties.
Full member coverage
It works with private, protected, internal, and public members, as well as static and instance targets.
Multiple attributes with deterministic ordering
It supports stacking multiple attributes on the same member. Execution order is determined first by the Priority property (ascending), then by declaration order (topmost applied attribute executes first for equal priorities).
Rich execution context
It provides read‑only access to declaring type and member metadata, method parameters and their runtime values, application IoC and a property bag for sharing data between the aspect's methods (per aspect instance).
Asp.Net like pipelines for all class members
It can create asp.net like pipelines by using multiple aspects on class members.
Exception handling
It captures thrown exceptions and allows aspects to inspect, handle, suppress, or replace them.
Return value interception
It enables inspection and modification of return values before they reach the caller. For async methods, it only allows adding continuations to the original task.
Shared attribute state
Allows attribute instances to maintain state across their lifecycle during a single invocation.
Comprehensive async support
Fully supports Task, Task<T>, ValueTask and ValueTask<T>.
Support inheritance
When aspects are used to decorate a class, the aspects are applied on inherited members even if they are not directly implemented in the class.
Inherited members can be overwritten or new members can be declared which will overwrite inherited methods and their aspects.
Debugger‑friendly
Preserves debugging breakpoints in both user code and aspect code.
How it works
AspectNet weaves aspects into the target assembly immediately after the build completes, using IL rewriting.
Assuming the following class:
public partial class OrderService
{
[RecordActivity(Priority = 10)]
public void PlaceOrder()
{
Console.WriteLine("PlaceOrder called");
if (DateTime.Now == DateTime.Parse("2000-01-01"))
throw new Exception();
}
}
The final code in the assembly will be (comments added for readability only):
public partial class OrderService
{
[ProcessedByAspectNet]
public void PlaceOrder()
{
AspectNetAttributeContext aspectNetAttributeContext = new AspectNetAttributeContext();
aspectNetAttributeContext.MemberName = "PlaceOrder";
aspectNetAttributeContext.Instance = this;
aspectNetAttributeContext.ClassType = GetType();
RecordActivityAttribute recordActivityAttribute = new RecordActivityAttribute();
recordActivityAttribute.Priority = 10;
/* other aspect properties settings if any */
try
{
recordActivityAttribute.OnEntry(aspectNetAttributeContext);
/* -- Original code -- */
Console.WriteLine("PlaceOrder called");
if (DateTime.Now == DateTime.Parse("2000-01-01"))
{
throw new Exception();
}
/* -- End of original code -- */
recordActivityAttribute.OnSuccess(aspectNetAttributeContext);
}
catch (Exception ex)
{
Exception ex2 = (aspectNetAttributeContext.Exception = ex);
recordActivityAttribute.OnException(aspectNetAttributeContext);
if (ex2 == aspectNetAttributeContext.Exception)
{
throw;
}
if (aspectNetAttributeContext.Exception != null)
{
throw aspectNetAttributeContext.Exception;
}
}
finally
{
recordActivityAttribute.OnExit(aspectNetAttributeContext);
}
}
}
Per‑project installation
Each project that uses AspectNet must import SimpleBitware.AspectNet NuGet package. This ensures correct incremental builds and parallel compilation across solutions.
Execution order
The execution order is aspects with lower Priority values first and, for the same priority, the ones which appear first (class then members, and top to bottom)
Using the following example, assuming Log, Exception, TrackProgress and Benchmark are all custom AspectNet aspects:
[Log(Priority = 10)]
public partial class OrderService
{
[TrackProgress]
[Exception]
[Benchmark(Priority = 1)]
public async Task PlaceOrderAsync(string id) { ... }
}
For all members of OrderService, including implicit constructor, except PlaceOrderAsync, only [Log] aspect will be executed:
OnEntry -> OnSuccess/OnException -> OnExit
For PlaceOrderAsync only the aspects execution order is:
- First [Benchmark] since it has the lowest priority.
- Second [Log] since it has the second lowest priority.
- Third [TrackProgress] since is doesn't have Priority property set and it uses the default priority value which is int.MaxValue. It's also first attribute of the method so it will be executed before the other aspects with the same Priority.
- Last one is [Exception] since is doesn't have Priority property set and it uses the default priority value which is int.MaxValue. It's also the last attribute of the method so will be executed after the other aspects with the same Priority.
Benchmark.OnEntry ->
Log.OnEntry ->
TrackProgress.OnEntry ->
Exception.OnEntry ->
{original method body} ->
Exception.OnSuccess/OnException -> Exception.OnExit ->
TrackProgress.OnSuccess/OnException -> TrackProgress.OnExit ->
Log.OnSuccess/OnException -> Log.OnExit ->
Benchmark.OnSuccess/OnException -> Benchmark.OnExit
Basic Usage
Each project that uses AspectNet must import SimpleBitware.AspectNet NuGet package. Classes within projects which do not import SimpleBitware.AspectNet NuGet package are not weaved, irrespective if they are decorated with AspectNet attributes.
dotnet add package SimpleBitware.AspectNet
Once package installed, use the built-in aspects (such as [Benchmark]) or create custom ones and decorate class members or entire class. All class members are supported except fields.
[Log]
public partial class OrderService
{
[Benchmark(Priority = 1)]
public async Task PlaceOrderAsync(string id) { ... }
}
Create custom aspects
The recommended way to create a custom aspects is to create an attribute extending AbstractAspectNetAttribute abstract class.
This offers the flexibility to implement only the necessary methods and provides access to the application ServiceProvider when UseAspectNet extension is used.
Alternatively, at minimum, an aspect attribute needs to implement IAspectNetAttribute interface.
The library contains generic aspects such as BenchmarkAttribute, which can be used as example.
public class LogAttribute : AbstractAspectNetAttribute
{
public override void OnEntry(AspectNetAttributeContext context)
{
base.OnEntry(context);
}
public override void OnSuccess(AspectNetAttributeContext context)
{
base.OnSuccess(context);
}
public override void OnException(AspectNetAttributeContext context)
{
base.OnException(context);
}
public override void OnExit(AspectNetAttributeContext context)
{
base.OnExit(context);
}
}
Debugging
Weaving process control is done via configuration in SimpleBitware.AspectNet.props file. This file can be found in the local nuget repository folder, under simplebitware.aspectnet\{version}\buildfolder.
Set DisableAspectNet to true to disable weaving.
Set LogLevel to Debug to show weaving logs in the build window.
Set LogLevel to Trace to generate the before and after IL files.
By default, only exceptions thrown during weaving process are logged in the build window.
Repositories
Contribute
Please visit product forum