【问题标题】:Autofac method level interception with Castle DynamicProxy in .NET Core 2在 .NET Core 2 中使用 Castle DynamicProxy 进行 Autofac 方法级拦截
【发布时间】:2018-06-23 14:57:16
【问题描述】:

我目前写了一个拦截器,代码如下

public class TransactionalInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        using (var transaction = ...)
        {
            try
            {
                invocation.Proceed();
                transaction.Commit();

            }
            catch
            {
                transaction.Rollback();
            }
            finally
            {
                transaction.Dispose();
            }
        }

    }
}

但是当注册这个拦截器时,它将适用于所有方法。我有一个服务类,其存储库注入了 CRUD 方法。 我不希望为查询方法打开事务。

我阅读了这个链接,但我不知道如何将它应用到我的代码中 http://docs.autofac.org/en/latest/advanced/adapters-decorators.html#decorators

我不知道谁来重构我的 TransactionalInterceptor(并注册它)以在像这段代码这样的类中使用它

[Intercept(typeof(LoggerInterceptor))] //logger
public class SomeService : ISomeService
{
    private readonly ISomeRepository someRepository;

    public SomeService(SomeRepository someRepository)
    {
        this.someRepository = someRepository;
    }

    public IEnumerable<SomeDto> GetAll()
    {
        // code
    }

    public SomeDto GetById()
    {
        // code
    }

    [Transactional]
    public int Create(SomeDto someDto)
    {
        // code to insert
    }
}

【问题讨论】:

  • IIRC,只拦截virtual方法。
  • @Amy 你几乎是对的。 Castle 有 2 种内部方法来进行拦截。一种用于class,其中仅拦截virtual 成员,另一种用于interface,其中将拦截接口的所有方法。在第一种情况下,将创建一个继承类,在另一种情况下,将创建一个实现接口的聚合类型。

标签: c# autofac castle-dynamicproxy


【解决方案1】:

Intercept 方法的invocation 参数包含一个Method 属性,它是当前拦截的方法的MethodInfo

您可以使用此属性来做您想做的事。

例如通过使用方法名:

public void Intercept(IInvocation invocation)
{
    if (invocation.MethodInvocationTarget.Name != nameof(ISomeService.Create))
    {
        invocation.Proceed();
        return;
    }
    using (var transaction = ...)
    {
        try
        {
            invocation.Proceed();
            transaction.Commit();
        }
        catch
        {
            transaction.Rollback();
        }
        finally
        {
            transaction.Dispose();
        }
    }
}

或基于目标方法的属性:

if (!invocation.MethodInvocationTarget
               .CustomAttributes
               .Any(a => a.AttributeType == typeof(TransactionalAttribute)))

您也可以使用IInterceptorSelector 类型,但需要更多工作才能将其注册到Autofac

【讨论】:

  • 谢谢@cyril。我去检查CustomAttributes。我对您的回答所做的唯一更改是将invocation.Method 更改为invocation.MethodInvocationTarget
【解决方案2】:

我用ProxyGenerationHook 解决了这个问题。见answer

  1. 创建自定义属性以选择拦截的方法。该属性的目标应该是Method
    [System.AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
    sealed class UseInterceptorAttribute : Attribute
    {
        public UseInterceptorAttribute()
        {
        }
    }
  1. 创建您的服务接口和服务类:
    public interface ISomeService
    {
        void GetWithoutInterceptor();

        [UseInterceptor]
        void GetWithInterceptor();
    }

    public class SomeService
    {
        void GetWithoutInterceptor()
        {
            //This method will not be intercepted...
        }

        [UseInterceptor]
        void GetWithInterceptor()
        {
            //This method will be intercepted...
        }
    }
  1. 创建您的ProxyGenerationHook
    public class SomeServiceProxyGenerationHook : IProxyGenerationHook
    {
        public void MethodsInspected()
        {
        }

        public void NonProxyableMemberNotification(Type type, MemberInfo memberInfo)
        {
        }

        public bool ShouldInterceptMethod(Type type, MethodInfo methodInfo)
        {
            return methodInfo
                .CustomAttributes
                .Any(a => a.AttributeType == typeof(UseInterceptorAttribute));
        }
    }
  1. 不要使用属性来启用拦截器。启用它时 像这样注册您的服务:
    public class AutofacDependencyResolver
    {
        private readonly IContainer _container;
        public AutofacDependencyResolver()
        {
            _container = BuildContainer();
        }

        private IContainer BuildContainer()
        {
            var proxyGenerationOptions = new ProxyGenerationOptions(new ProductServiceProxyGenerationHook());

            builder.RegisterType<SomeService>()
                .As<ISomeService>()
                .EnableInterfaceInterceptors(proxyGenerationOptions)
                .InterceptedBy(typeof(TransactionalInterceptor))

            builder.Register(c => new TransactionalInterceptor());
            return builder.Build();
        }

        public T GetService<T>()
            where T:class
        {
            var result = _container.TryResolve(out T serviceInstance);
            return serviceInstance ?? throw new Exception($"The service could not found: {nameof(T)}");
        }
    }

此解决方案遵循此article

我还上传了有关此解决方案的最小example

【讨论】:

    【解决方案3】:

    也可以试试,很简单https://fs7744.github.io/Norns.Urd/index.html

    public class AddTenInterceptorAttribute : AbstractInterceptorAttribute
    {
        public override void Invoke(AspectContext context, AspectDelegate next)
        {
            next(context);
            AddTen(context);
        }
    
        private static void AddTen(AspectContext context)
        {
            if (context.ReturnValue is int i)
            {
                context.ReturnValue = i + 10;
            }
            else if(context.ReturnValue is double d)
            {
                context.ReturnValue = d + 10.0;
            }
        }
    
        public override async Task InvokeAsync(AspectContext context, AsyncAspectDelegate next)
        {
            await next(context);
            AddTen(context);
        }
    }
    
    
    [AddTenInterceptor]
    public interface IGenericTest<T, R> : IDisposable
    {
        // or
        //[AddTenInterceptor]
        T GetT();
    }
    

    【讨论】:

      猜你喜欢
      • 2012-03-31
      • 2023-03-03
      • 1970-01-01
      • 2018-10-06
      • 2011-04-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-26
      相关资源
      最近更新 更多