Asp.net Core 3.1 AspectCore 기반 AOP 구현 트 랜 잭 션,캐 시 차단기 기능

최근 에 제 프레임 워 크 에 하나의 기능 을 추가 하고 싶 습 니 다.예 를 들 어 하나의 방법 에 업무 의 특성 인 Attribute 를 추가 하면 이 방법 은 업무 처 리 를 사용 합 니 다.하나의 방법 에 캐 시 기능 을 추가 하면 이 방법 은 캐 시 를 진행 할 것 이다.
이것 도 인터넷 에서 말 하 는 절단면 프로 그래 밍 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 구현 트 랜 잭 션,캐 시 차단기 내용 은 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 찾 아 보 세 요.앞으로 도 많은 응원 부 탁 드 리 겠 습 니 다!

좋은 웹페이지 즐겨찾기