Skip to content

[API Proposal]: Custom attribute for Pre-compile AOP #59454

Open
@wsq003

Description

@wsq003

Background and motivation

[MyCache(DurationSeconds = 5)]
public string GetUserName(int userID)
{
    //load from db
    return "Mike";
}
[MyPerfLog()]
public bool Login(string userName, string password)
{
    //check with db
    return true;
}

It is very annoying to add AOP to functions.
For now, you need to:
1, Create a custom Attribute
2, Create a custom Interceptor to handle your custom Attribute
3, Register your Interceptor at application startup

I think there would be a syntactic sugar that handling specific attribute.

API Proposal

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public abstract class AopAttribute : Attribute
{
	/// <summary>
	/// </summary>
	/// <returns>if execute the real Method or not</returns>
	public abstract bool BeforeMethod(MethodInfo info);
	public abstract void AfterMethod(ResultInfo result);

	public abstract Task<bool> BeforeMethodAsync(MethodInfo info);
	public abstract Task AfterMethodAsync(ResultInfo result);
}

This is a compile-time attribute, more or less like System.Diagnostics.ConditionalAttribute.
Compiler will generate proxy class to wrap the target functions.

API Usage

public class MyPerfLogAttribute : AopAttribute
{
	ILogger _logger;
	DateTime _begin;

	public MyPerfLogAttribute(ILogger logger)
	{
		_logger = logger;
	}

	public override bool BeforeMethod(MethodInfo info)
	{
		_begin = DateTime.Now;
		return true;
	}

	public override async Task<bool> BeforeMethodAsync(MethodInfo info)
	{
		_begin = DateTime.Now;
		await Task.CompletedTask;
		return true;
	}

	public override void AfterMethod(ResultInfo result)
	{
		var span = DateTime.Now - _begin;
		_logger.LogInformation($"use {span.TotalMilliseconds:0.00}");
	}

	public override async Task AfterMethodAsync(ResultInfo result)
	{
		var span = DateTime.Now - _begin;
		_logger.LogInformation($"use {span.TotalMilliseconds:0.00}");
		await Task.CompletedTask;
	}
}
[MyPerfLog()]
public bool Login(string userName, string password)
{
    //check with db
    return true;
}

Then the compiler will take care the rest.

Risks

  1. Application Performance: should not be affected.
  2. Compiler complexity: should be fine.
  3. Extensibility: should be fine.
  4. Applicability: AOP is a very common requirement. Such compiler supported pre-compile AOP attribute should be very sweet.

Alternative

  1. A built-in interceptor can also do such things easily. A built-in interceptor will not need to change compiler, but will slow down the application startup speed. Application startup speed is important in this docker(container) era.

Metadata

Metadata

Assignees

No one assigned

    Labels

    api-needs-workAPI needs work before it is approved, it is NOT ready for implementationapi-suggestionEarly API idea and discussion, it is NOT ready for implementationarea-System.Runtime.CompilerServices

    Type

    No type

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions