Asp.net Core 3.1 AspectCore 기반 AOP 구현 트 랜 잭 션,캐 시 차단기 기능
이것 도 인터넷 에서 말 하 는 절단면 프로 그래 밍 AOP 입 니 다.
AOP 의 개념 도 잘 이해 되 고 미들웨어 와 차이 가 많 지 않 습 니 다.말하자면 제 가 임의로 방법의 앞 이나 뒤에 코드 를 추가 할 수 있 습 니 다.이것 은 캐 시,로그 등 처리 에 적합 합 니 다.
net core 2.2 때 저 는 autofac 로 op 을 실현 하려 고 시 도 했 지만 이번 에는 autofac 를 사용 하고 싶 지 않 습 니 다.저 는 더욱 가 벼 운 프레임 워 크,AspectCore 를 사 용 했 습 니 다.
사용 하기에 매우 간단 하지만 처음에 약간의 시행 착 오 를 걸 었 다.주로 인터넷 은 모두 net core 3 이하 의 튜 토리 얼 이 고 3 이하 의 사용 방법 은 이전 과 약간 다르다.
NuGet 패키지 먼저 설치,패키지 이름:AspectCore.Extensions.Dependency Injection
그리고 Program.cs 클래스 에 코드 를 추가 합 니 다.이것 은 net core 3 의 다른 점 입 니 다.이 추가 코드 는 AspectCore 의 IOC 용기 로 내 장 된 것 을 교체 한 다 는 뜻 입 니 다.AOP 는 IOC 에 의존 해 야 하기 때문에 내 장 된 IOC 를 교체 해 야 한다.
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
// AspectCore IOC
.UseServiceProviderFactory(new DynamicProxyServiceProviderFactory());
}
그리고 Startup.cs 클래스 의 Configure Services 에 코드 를 추가 합 니 다.(사실 이것 은 추가 하거나 추가 하지 않 아 도 됩 니 다.설정 이 필요 하 다 면 추가 합 니 다.예 를 들 어 전체적인 차단기,일치 하 는 서비스 만 차단 합 니 다.저 는 특성 으로 만 차단 하기 때문에 아무것도 설정 하지 않 았 습 니 다)
services.ConfigureDynamicProxy(o=> {
// AOP
});
이렇게 하면 AOP 가 설정 되 어 있 습 니 다.간단 하지 않 습 니까?물론 사용 에 있어 서도 인터페이스,인터페이스의 방법,클래스,클래스 의 virtual 방법 에서 차단 할 수 있 음 을 주의해 야 한다.그리고 컨트롤 러 액 션 을 차단 하려 면 Configure Service 에서 AddController AsServices 가 필요 합 니 다.
services.AddControllers()
//
.AddControllersAsServices()
다음은 제 사무 차단기 코드 를 보 여 드 리 겠 습 니 다.특성 차단 이 라면 Abstract Interceptor Attribute 를 계승 하 겠 습 니 다.전역 차단 기 를 쓰 려 면 Abstract Interceptor 를 사용 하고 Configure Dynamic Proxy 에서 설정 하 겠 습 니 다.이 건 소개 하지 않 겠 습 니 다.만약 당신 의 차단기 가 다른 항목 에 놓 여 있다 면,AspectCore.Core 패 키 지 를 추가 하 는 것 을 기억 하 세 요.AspectCore.Abstractions 만 추가 하지 마 세 요.저 는 처음부터 AspectCore.Abstractions 만 추 가 했 습 니 다.IsAsync,UnwrapAsyncReturnValue 등 확장 방법 을 발견 하지 못 했 습 니 다.
public class TransactionInterceptorAttribute : AbstractInterceptorAttribute
{
public async override Task Invoke(AspectContext context, AspectDelegate next)
{
var dbContext = context.ServiceProvider.GetService<AppDbContext>();
//
if (dbContext.Database.CurrentTransaction == null)
{
await dbContext.Database.BeginTransactionAsync();
try
{
await next(context);
dbContext.Database.CommitTransaction();
}
catch (Exception ex)
{
dbContext.Database.RollbackTransaction();
throw ex;
}
}
else
{
await next(context);
}
}
}
그리고 저 는 이렇게 우아 하 게 사 무 를 사용 할 수 있 습 니 다.제 캐 시 차단 기 를 다시 보 여 드 리 겠 습 니 다.
public class CacheInterceptorAttribute : AbstractInterceptorAttribute
{
/// <summary>
///
/// </summary>
public int ExpireSeconds { get; set; }
public async override Task Invoke(AspectContext context, AspectDelegate next)
{
//
bool isAsync = context.IsAsync();
//if (context.ImplementationMethod.GetCustomAttribute(typeof(AsyncStateMachineAttribute)) != null)
//{
// isAsync = true;
//}
// ,
var methodReturnType = context.GetReturnParameter().Type;
if (methodReturnType == typeof(void) || methodReturnType == typeof(Task) || methodReturnType == typeof(ValueTask))
{
await next(context);
return;
}
var returnType = methodReturnType;
if (isAsync)
{
//
returnType = returnType.GenericTypeArguments.FirstOrDefault();
}
//
string param = CommonHelper.ObjectToJsonString(context.Parameters);
// , key
string key = "Methods:" + context.ImplementationMethod.DeclaringType.FullName + "." + context.ImplementationMethod.Name;
var cache = context.ServiceProvider.GetService<ICacheHelper>();
// ,
if (cache.HashExists(key, param))
{
// , cache.HashGet<>(key,param)
var value = typeof(ICacheHelper).GetMethod(nameof(ICacheHelper.HashGet)).MakeGenericMethod(returnType).Invoke(cache, new[] { key, param });
if (isAsync)
{
// Task ValueTask
if (methodReturnType == typeof(Task<>).MakeGenericType(returnType))
{
// Task<> , Task.FromResult(value)
context.ReturnValue = typeof(Task).GetMethod(nameof(Task.FromResult)).MakeGenericMethod(returnType).Invoke(null, new[] { value });
}
else if (methodReturnType == typeof(ValueTask<>).MakeGenericType(returnType))
{
// ValueTask<> , new ValueTask(value)
context.ReturnValue = Activator.CreateInstance(typeof(ValueTask<>).MakeGenericType(returnType), value);
}
}
else
{
context.ReturnValue = value;
}
return;
}
await next(context);
object returnValue;
if (isAsync)
{
returnValue = await context.UnwrapAsyncReturnValue();
// , (context.ReturnValue as Task<>).Result
//returnValue = typeof(Task<>).MakeGenericType(returnType).GetProperty(nameof(Task<object>.Result)).GetValue(context.ReturnValue);
}
else
{
returnValue = context.ReturnValue;
}
cache.HashSet(key, param, returnValue);
if (ExpireSeconds > 0)
{
cache.SetExpire(key, TimeSpan.FromSeconds(ExpireSeconds));
}
}
}
캐 시 삭제 차단 기 를 만 들 었 습 니 다.이 기능 을 가 진 방법 으로 실행 하면 캐 시 값 을 삭제 합 니 다.왜 이 디자인 이 있 습 니까?예 를 들 어 제 가 GetUserList 에 캐 시 를 넣 었 습 니 다.그러면 제 데이터 가 바 뀌 면 어떻게 합 니까?저 는 User 데이터 가 바 뀌 었 을 때 이 캐 시 를 삭제 하고 싶 습 니 다.그러면 저 는 SaveUser 방법 에 이 캐 시 를 추가 하여 차단 기 를 삭제 할 수 있 습 니 다.그러면 이 방법 이 실 행 된 후에 관련 캐 시 를 삭제 할 것 입 니 다.
public class CacheDeleteInterceptorAttribute : AbstractInterceptorAttribute
{
private readonly Type[] _types;
private readonly string[] _methods;
/// <summary>
/// Types Methods, Type Method key,
/// </summary>
/// <param name="Types"> </param>
/// <param name="Methods"> , Types </param>
public CacheDeleteInterceptorAttribute(Type[] Types, string[] Methods)
{
if (Types.Length != Methods.Length)
{
throw new ApiFailException(ApiFailCode.OPERATION_FAIL, "Types Methods ");
}
_types = Types;
_methods = Methods;
}
public async override Task Invoke(AspectContext context, AspectDelegate next)
{
var cache = context.ServiceProvider.GetService<ICacheHelper>();
await next(context);
for (int i = 0; i < _types.Length; i++)
{
var type = _types[i];
var method = _methods[i];
string key = "Methods:" + type.FullName + "." + method;
cache.Delete(key);
}
}
}
AOP 의 실현 원 리 를 나 도 상상 해 봤 다.AOP 를 실현 하려 면 IOC 용기 에 의존 해 야 한다.왜냐하면 그것 은 우리 류 의 집사 이기 때문이다.차단 할 수 있 는 종 류 는 반드시 IOC 가 주입 해 야 하고 자신 이 새로 나 온 것 은 차단 되 지 않 는 다.만약 에 제 가 A 방법 앞 에 코드 를 추가 하고 싶다 면 IOC 에 코드 를 알려 드 리 겠 습 니 다.그러면 IOC 는 A 방법 이 있 는 종 류 를 주입 할 때 파생 류 를 생 성하 고 A 방법 을 다시 쓰 기 때문에 차단 방법 은 반드시 virtual 이 어야 합 니 다.그리고 A 방법 에 제 가 추가 할 코드 를 쓰 고 base.A()이렇게 해 야 합 니 다.
Asp.net Core 3.1 AspectCore 기반 AOP 구현 트 랜 잭 션,캐 시 차단기 기능 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.더 많은 관련 Asp.net Core 3.1 구현 트 랜 잭 션,캐 시 차단기 내용 은 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 찾 아 보 세 요.앞으로 도 많은 응원 부 탁 드 리 겠 습 니 다!