Nuget package address: https://www.nuget.org/packages/CZGL.AOP/
Github repository address: https://github.com/whuanle/CZGL.AOP
CZGL.AOP is a simple and lightweight AOP framework written based on EMIT, supporting non-invasive interception, .NET Core/ASP.NET Core, and various dependency injection frameworks.
1. Quick Start
CZGL.AOP is quite simple to use. You only need to mark the types to be intercepted with [Interceptor]
and then use the inherited ActionAttribute
to mark the methods or properties to be intercepted.
1.1 Inheriting ActionAttribute Attributes
ActionAttribute
is an attribute used for intercepting methods or properties and cannot be used directly; it needs to be inherited and methods overridden.
An example is as follows:
public class LogAttribute : ActionAttribute
{
public override void Before(AspectContext context)
{
Console.WriteLine("Before execution");
}
public override object After(AspectContext context)
{
Console.WriteLine("After execution");
if (context.IsMethod)
return context.MethodResult;
else if (context.IsProperty)
return context.PropertyValue;
return null;
}
}</code></pre>
Before
will take effect before the intercepted method is executed or the intercepted property is called. You can obtain and modify the passed parameters through the AspectContext
context.
After
takes effect after the method is executed or the property is called; you can obtain and modify the return value through the context.
1.2 Marking Interceptor Types
In the types to be intercepted, use the [Interceptor]
attribute to mark them, and in the methods that need to be intercepted, use the attributes inherited from ActionAttribute
to mark them.
This method is invasive and must be completed before compilation.
[Interceptor]
public class Test : ITest
{
[Log] public virtual string A { get; set; }
[Log]
public virtual void MyMethod()
{
Console.WriteLine("Running");
}
}
Note that a method or property can only have one interceptor set.
2. How to Create Interceptor Types
CZGL.AOP offers various ways to generate interceptor types. Below, I will introduce the simplest method.
Please pre-create the following code:
public class LogAttribute : ActionAttribute
{
public override void Before(AspectContext context)
{
Console.WriteLine("Before execution");
}
public override object After(AspectContext context)
{
Console.WriteLine("After execution");
if (context.IsMethod)
return context.MethodResult;
else if (context.IsProperty)
return context.PropertyValue;
return null;
}
}
public interface ITest
{
void MyMethod();
}
[Interceptor]
public class Test : ITest
{
[Log] public virtual string A { get; set; }
public Test()
{
Console.WriteLine("Constructor is fine");
}
[Log]
public virtual void MyMethod()
{
Console.WriteLine("Running");
}
}</code></pre>
2.1 Creating Directly via API
Using the AopInterceptor
class in CZGL.AOP, you can generate interceptor types.
An example is as follows:
ITest test1 = AopInterceptor.CreateProxyOfInterface<ITest, Test>();
Test test2 = AopInterceptor.CreateProxyOfClass<Test>();
test1.MyMethod();
test2.MyMethod();
CreateProxyOfInterface
creates an interceptor type via an interface; CreateProxyOfClass
creates an interceptor type via a class.
By default, it calls the parameterless constructor.
2. Creating Interceptor Types
Via API
You can reference the ExampleConsole project in the source code solution.
To directly use AopInterceptor.CreateProxyOfInterface
and AopInterceptor.CreateProxyOfClass
methods to create interceptor types via an interface or a class.
ITest test1 = AopInterceptor.CreateProxyOfInterface<ITest, Test>();
Test test2 = AopInterceptor.CreateProxyOfClass<Test>();
If you need to specify the constructor to be instantiated, you can do this:
// Specify the constructor
test2 = AopInterceptor.CreateProxyOfClass<Test>("aaa", "bbb");
test2.MyMethod();
Via Microsoft.Extensions.DependencyInjection
Microsoft.Extensions.DependencyInjection
is the default dependency injection container for .NET Core/ASP.NET Core.
If you want to support the use of AOP in ASP.NET Core, you can install the package CZGL.AOP.MEDI
from Nuget.
If you use Microsoft.Extensions.DependencyInjection
in a console application, you can use the extension method BuildAopProxy
on IServiceCollection
to generate interceptor types for the types in the container.
An example is as follows:
IServiceCollection _services = new ServiceCollection();
_services.AddTransient<ITest, Test>();
var serviceProvider = _services.BuildAopProxy().BuildServiceProvider();
serviceProvider.GetService<ITest>();
return serviceProvider;
You can refer to the ExampleMEDI
project in the source code solution.
If you want to use it in ASP.NET Core, you can use services.BuildAopProxy();
as the last line in the ConfigureServices
method of Startup
.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.BuildAopProxy();
}
You can also configure the use of CZGL.AOP in the Program
's IHostBuilder
using .UseServiceProviderFactory(new AOPServiceProxviderFactory())
.
Example:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AOPServiceProxviderFactory())
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
You can refer to the ExampleConsole
and ExampleWebMEDI
projects in the solution.
You need not worry that using dependency injection with CZGL.AOP will slow down the program or damage the original properties in the container. CZGL.AOP will only process the types that need to be intercepted when creating the container, and will not affect the services in the container or interfere with the execution of dependency injection.
Via Autofac
If you need to use AOP in Autofac, you need to reference the CZGL.AOP.Autofac
package.
If you use Autofac in a console program, you can use BuildAopProxy()
after Build()
.
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<Test>().As<ITest>();
var container = builder.Build().BuildAopProxy();
using (ILifetimeScope scope = container.BeginLifetimeScope())
{
// Get instance
ITest myService = scope.Resolve<ITest>();
myService.MyMethod();
}
Console.ReadKey();
}</code></pre>
Note that you must call BuildAopProxy()
after the completion of component registration to create a new container,
so that you can consider whether to intercept the components in the new container.
If using Autofac in ASP.NET Core, you need to use:
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
If you need to intercept already registered components, replace it with:
.UseServiceProviderFactory(new CZGL.AOP.Autofac.AOPServiceProxviderFactory())
Please refer to the ExampleAutofac
and ExampleWebAutofac
projects in the source code solution.
3. In-depth Usage
Interceptor Types
The types to be intercepted need to be marked with [Interceptor]
, for example:
[Interceptor]
public class Test : ITest
{
}
Generic types are supported.
The types to be intercepted must be inheritable.
There are no restrictions on the constructor of the type; you can write it freely.
When using the API to create interceptor types and instantiate them, you can specify which constructor to use.
For example:
string a="",b="",c="";
ITest test1 = AopInterceptor.CreateProxyOfInterface<ITest, Test>(a,b,c);
The API will automatically look for the appropriate constructor based on the number and type of the parameters.
Method and Property Interceptors
To intercept methods or properties, you need to inherit from the ActionAttribute
attribute, then mark this attribute for the method or property, and set the method or property to virtual
.
Different methods in a type can use different interceptors.
[Log1]
public virtual void MyMethod1(){}
[Log2]
public virtual void MyMethod2(){}</code></pre>
For properties, you can use the attribute directly on the property or only on the get or set accessor.
[Log] public virtual string A { get; set; }
// Or
public virtual string A { [Log] get; set; }
// Or
public virtual string A { get; [Log] set; }</code></pre>
If using the attribute on a property, it is equivalent to [Log] get; [Log] set;
.
Context
A simple method or property interception attribute looks like this:
public class LogAttribute : ActionAttribute
{
public override void Before(AspectContext context)
{
Console.WriteLine("Before execution");
}
public override object After(AspectContext context)
{
Console.WriteLine("After execution");
if (context.IsMethod)
return context.MethodResult;
else if (context.IsProperty)
return context.PropertyValue;
return null;
}
}
The properties of AspectContext are explained as follows:
Field
Description
Type
The proxy type generated from the current intercepted type
ConstructorParamters
The parameters used by the constructor when the type is instantiated; if the constructor has no parameters, MethodValues.Length
will be 0, and MethodValues
will not be null.
IsProperty
This indicates that the current interception is for a property
PropertyInfo
Information about the currently executed property, which may be null
PropertyValue
When the property is called, returns the result of get or the value of set.
IsMethod
The current interception is a method.
MethodInfo
Information about the current method.
MethodValues
Parameters passed when the method is called. If this method has no parameters, then MethodValues.Length = 0, rather than MethodValues being null.
MethodResult
The result returned by the method execution (if any).
Parameters for Intercepting Methods or Properties
Through the context, you can modify the parameters of methods or properties and intercept the return results:
public class LogAttribute : ActionAttribute
{
public override void Before(AspectContext context)
{
// Intercept and modify the method parameters
for (int i = 0; i < context.MethodValues.Length; i++)
{
context.MethodValues[i] = (int)context.MethodValues[i] + 1;
}
Console.WriteLine("Before execution");
}
public override object After(AspectContext context)
{
Console.WriteLine("After execution");
// Intercept the result of the method execution
context.MethodResult = (int)context.MethodResult + 664;
if (context.IsMethod)
return context.MethodResult;
else if (context.IsProperty)
return context.PropertyValue;
return null;
}
}
[Interceptor]
public class Test
{
[Log]
public virtual int Sum(int a, int b)
{
Console.WriteLine("Running");
return a + b;
}
}</code></pre>
Test test = AopInterceptor.CreateProxyOfClass<Test>();
Console.WriteLine(test.Sum(1, 1));
The method parameters support in
, ref
, and out
; support generic methods and generic properties.
Non-Intrusive Proxies
This approach does not require changes to the type being proxied, and you can also proxy types within an assembly.
public class LogAttribute : ActionAttribute
{
public override void Before(AspectContext context)
{
Console.WriteLine("Before execution");
}
public override object After(AspectContext context)
{
Console.WriteLine("After execution");
if (context.IsMethod)
return context.MethodResult;
else if (context.IsProperty)
return context.PropertyValue;
return null;
}
}</code></pre>
public class TestNo
{
public virtual string A { get; set; }
public virtual void MyMethod()
{
Console.WriteLine("Running");
}
}
TestNo test3 = AopInterceptor.CreateProxyOfType<TestNo>(new ProxyTypeBuilder()
.AddProxyMethod(typeof(LogAttribute), typeof(TestNo).GetMethod(nameof(TestNo.MyMethod)))
.AddProxyMethod(typeof(LogAttribute), typeof(TestNo).GetProperty(nameof(TestNo.A)).GetSetMethod()));
Use ProxyTypeBuilder to build proxy types.
Proxy methods or properties are added using AddProxyMethod
, where the first parameter is the interceptor to be used, and the second parameter is the method to intercept.
If you want to intercept properties, please separate the settings for the get
and set
constructors.
If multiple methods or properties use the same interceptor, it can be done like this:
TestNo test3 = AopInterceptor.CreateProxyOfType<TestNo>(
new ProxyTypeBuilder(new Type[] { typeof(LogAttribute) })
.AddProxyMethod("LogAttribute", typeof(TestNo).GetMethod(nameof(TestNo.MyMethod)))
.AddProxyMethod("LogAttribute", typeof(TestNo).GetProperty(nameof(TestNo.A)).GetSetMethod()));
TestNo test3 = AopInterceptor.CreateProxyOfType<TestNo>(
new ProxyTypeBuilder(new Type[] { typeof(LogAttribute) })
.AddProxyMethod("LogAttribute", typeof(TestNo).GetMethod(nameof(TestNo.MyMethod)))
.AddProxyMethod(typeof(LogAttribute2), typeof(TestNo).GetProperty(nameof(TestNo.A)).GetSetMethod()));
Pass the required interceptors in the constructor, and use them during interception.
文章评论