【问题标题】:.NET Core default dependency injection with Castle DynamicProxy.NET Core 默认依赖注入与 Castle DynamicProxy
【发布时间】:2020-06-21 22:26:06
【问题描述】:

我有许多 AOP 库,它们使用 Castle DynamicProxy 和 Autofac DI 容器来进行日志记录、审计、事务控制等。

我想知道是否有一种方法可以使用默认的 .NET Core DI 容器来声明拦截器。拥有这种灵活性会很好,因为许多 .NET Core 项目不使用 Autofac。

【问题讨论】:

    标签: .net-core dependency-injection autofac aop castle-dynamicproxy


    【解决方案1】:

    基本 .NET Core 容器没有任何额外的功能,例如拦截器。 .NET Core 中的 DI 容器可以换成 Autofac 之类的东西的全部原因是,一旦超出默认容器,您就可以移动到不同的容器。

    【讨论】:

      【解决方案2】:

      是的,您可以通过 Core DI 使用 DynamicProxy。我在http://codethug.com/2021/03/17/Caching-with-Attributes-in-DotNet-Core5/ 写了一篇博文解释它,但这里是它的代码:

      创建属性

      [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
      public class CacheAttribute : Attribute
      {
          public int Seconds { get; set; } = 30;
      }
      

      创建一个拦截器(需要 Castle.Core nuget 包)

      public class CacheInterceptor : IInterceptor
      {
          private IMemoryCache _memoryCache;
          public CacheInterceptor(IMemoryCache memoryCache)
          {
              _memoryCache = memoryCache;
          }
      
          // Create a cache key using the name of the method and the values
          // of its arguments so that if the same method is called with the
          // same arguments in the future, we can find out if the results 
          // are cached or not
          private static string GenerateCacheKey(string name, 
              object[] arguments)
          {
              if (arguments == null || arguments.Length == 0)
                  return name;
              return name + "--" + 
                  string.Join("--", arguments.Select(a => 
                      a == null ? "**NULL**" : a.ToString()).ToArray());
          }
      
          public void Intercept(IInvocation invocation)
          {
              var cacheAttribute = invocation.MethodInvocationTarget
                  .GetCustomAttributes(typeof(CacheAttribute), false)
                  .FirstOrDefault() as CacheAttribute;
      
              // If the cache attribute is added ot this method, we 
              // need to intercept this call
              if (cacheAttribute != null)
              {
                  var cacheKey = GenerateCacheKey(invocation.Method.Name, 
                      invocation.Arguments);
                  if (_memoryCache.TryGetValue(cacheKey, out object value))
                  {
                      // The results were already in the cache so return 
                      // them from the cache instead of calling the 
                      // underlying method
                      invocation.ReturnValue = value;
                  }
                  else
                  {
                      // Get the result the hard way by calling 
                      // the underlying method
                      invocation.Proceed();
                      // Save the result in the cache
                      var options = new MemoryCacheEntryOptions
                      {
                          AbsoluteExpirationRelativeToNow = 
                              new System.TimeSpan(hours: 0, minutes: 0, 
                                  seconds: cacheAttribute.Seconds)
                      };
                      _memoryCache.Set(cacheKey, invocation.ReturnValue, 
                          options);
                  }
              }
              else
              {
                  // We don't need to cache the results, 
                  // nothing to see here
                  invocation.Proceed();
              }
          }
      }
      

      添加扩展方法以帮助在 DI 中注册类:

      public static void AddProxiedScoped<TInterface, TImplementation>
          (this IServiceCollection services)
          where TInterface : class
          where TImplementation : class, TInterface
      {
          // This registers the underlying class
          services.AddScoped<TImplementation>();
          services.AddScoped(typeof(TInterface), serviceProvider =>
          {
              // Get an instance of the Castle Proxy Generator
              var proxyGenerator = serviceProvider
                  .GetRequiredService<ProxyGenerator>();
              // Have DI build out an instance of the class that has methods
              // you want to cache (this is a normal instance of that class 
              // without caching added)
              var actual = serviceProvider
                  .GetRequiredService<TImplementation>();
              // Find all of the interceptors that have been registered, 
              // including our caching interceptor.  (you might later add a 
              // logging interceptor, etc.)
              var interceptors = serviceProvider
                  .GetServices<IInterceptor>().ToArray();
              // Have Castle Proxy build out a proxy object that implements 
              // your interface, but adds a caching layer on top of the
              // actual implementation of the class.  This proxy object is
              // what will then get injected into the class that has a 
              // dependency on TInterface
              return proxyGenerator.CreateInterfaceProxyWithTarget(
                  typeof(TInterface), actual, interceptors);
          });
      }
      

      将这些行添加到 Startup.cs 中的 ConfigureServices

      // Setup Interception
      services.AddSingleton(new ProxyGenerator());
      services.AddScoped<IInterceptor, CacheInterceptor>(
      

      之后,如果要使用缓存拦截器,需要做两件事:

      首先,将属性添加到您的方法中

      [Cache(Seconds = 30)]
      public async Task<IEnumerable<Person>> GetPeopleByLastName(string lastName)
      {
          return SomeLongRunningProcess(lastName);
      }
      

      其次,使用代理/拦截在DI中注册类:

      services.AddProxiedScoped<IPersonRepository, PersonRepository>();
      

      而不是没有代理/拦截的正常方式:

      services.AddScoped<IPersonRepository, PersonRepository>();
      

      【讨论】:

        猜你喜欢
        • 2016-05-22
        • 1970-01-01
        • 2018-03-23
        • 2021-10-23
        • 1970-01-01
        • 2021-02-02
        • 1970-01-01
        • 2020-12-20
        • 1970-01-01
        相关资源
        最近更新 更多