.net8 AOP 實現簡單的Json Redis 快取/業務分離

筑基期發表於2024-09-05

場景: 在不動 業務邏輯的情況下, 實現讀寫快取

   這裡使用的AOP的方式 在 方法上加註解, 這目前有個弊端不能傳陣列, 涉及到排序問題

  /// <summary>
  /// Json Redis快取
  /// </summary>
  public class RedisInterceptorAttribute : AbstractInterceptorAttribute
  { 
      readonly string _cacheKey;
      bool _isDb = false; //是否走快取
      int _expireSeconds = -1;
   

      public RedisInterceptorAttribute(string cacheKey, int expireSeconds = -1,bool isDb=false)
      {
          _cacheKey = cacheKey;
          _isDb = isDb;
          _expireSeconds = expireSeconds;
      }

      public override async Task Invoke(AspectContext context, AspectDelegate next)
      {
         
          Type returnType = context.ServiceMethod.ReturnType;
          Type genericArgument = returnType.GetGenericArguments()[0];
          // 生成快取的key
          string cacheKeyWithParams = GenerateCacheKey(context);

          if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>))
          {
              //透過型別獲取快取方法,並且呼叫 GetAsync 方法 
              MethodInfo method = this.GetType().GetMethod("GetAsync", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
              MethodInfo genericMethod = method.MakeGenericMethod(genericArgument);

              var cacheTask = (Task)genericMethod.Invoke(this, new object[] { cacheKeyWithParams });
              await cacheTask.ConfigureAwait(true);

              var resultProperty = cacheTask.GetType().GetProperty("Result");
              var cacheResult = resultProperty.GetValue(cacheTask);


              if (cacheResult != null)
              {
                  context.ReturnValue = ConvertToReturnType(cacheResult, returnType);
                  // 設定快取的邏輯
                  await Task.CompletedTask;

              }
              else
              {
                  await context.Invoke(next);
                  // 設定快取
                   if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>))
                  {
                      MethodInfo setMethod = this.GetType().GetMethod("SetAsync", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
                      MethodInfo genericSetMethod = setMethod.MakeGenericMethod(genericArgument);

                      var resultProperty1 = context.ReturnValue.GetType().GetProperty("Result");
                      var result = resultProperty1.GetValue(context.ReturnValue);
                      await (Task)genericSetMethod.Invoke(this, new object[] { cacheKeyWithParams, result });
                  }
              }
          }
          else
          {
              await next(context);
          }
      }
      private string GenerateCacheKey(AspectContext context)
      {
          var keyBuilder = new System.Text.StringBuilder(_cacheKey);

          foreach (var parameter in context.Parameters)
          {
              keyBuilder.Append(":").Append(FormatParameter(parameter));
          }

          return keyBuilder.ToString();
      }

      /// <summary>
      /// 格式化引數(類似url過濾一下資料,: 會影響閱讀)
      /// </summary>
      /// <param name="parameter"></param>
      /// <returns></returns>
      private string FormatParameter(object parameter)
      {
          
          return JsonConvert.SerializeObject(parameter).Replace(":", "=").Replace("{", "").Replace("}", "").Replace("\"", "").Replace(",", "&");
      }
      /// <summary>
      /// 將返回值轉換為原有型別
      /// </summary>
      /// <param name="result"></param>
      /// <param name="returnType"></param>
      /// <returns></returns>
      private object ConvertToReturnType(object result, Type returnType)
      {
          Type taskResultType = returnType.GetGenericArguments()[0];
          return typeof(Task).GetMethod(nameof(Task.FromResult))
                             .MakeGenericMethod(taskResultType)
                             .Invoke(null, new[] { result });
      }

      async Task<T> GetAsync<T>(string key)
      {
          var result = await RedisHelper.GetAsync<T>(key);
          return result;
      }

      async Task SetAsync<T>(string cacheKey, T data)
      {
          if (data != null)
          {
              await RedisHelper.SetAsync(cacheKey, data, _expireSeconds);
          }
      }

  }

這樣就簡單實現了

只需要在方法上加註解, 一定要加virtual

     //一定要加virtual
     [RedisInterceptor("school")]
     public virtual async Task<List<string>> SelectAsync(Guid[] schoolId)
     {
         ///業務
         return new();
     }

QQ群: 929412850 (Byte.Core 框架交流群)
未經作者同意,禁止轉發

相關文章