dotnetcore/AspectCore-Framework

当调用未被代理的方法且类的访问级别不是 public 时出错

devenliu opened this issue · 3 comments

代码

/*********************
* MyWeb.csproj
**********************/
using AspectCore.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;

namespace MyWeb
{
    // 启动类
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .UseServiceProviderFactory(new DynamicProxyServiceProviderFactory())  // 替换 DI 工厂
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }

    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.ConfigureDynamicProxy(config => 
            {
                // 注册全局拦截器(只拦截被打了标记的方法)
                config.Interceptors.AddTyped<TransactionalInterceptorAttribute>((MethodInfo method) => method.GetCustomAttribute<TransactionFlagAttribute>() != null);
            })
        }
    }
}

/*********************
* MyLib.csproj
**********************/
// 定义标记
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class TransactionFlagAttribute: Attribute
{

}

// 定义拦截器
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class TransactionInterceptorAttribute : AbstractInterceptorAttribute
{
    public override async Task Invoke(AspectContext context, AspectDelegate next)
    {
        // 启用事务...
        await next(context);
        // 提交事务...
    }
}

// 定义服务
public interface ICustomService
{
    // 给需要被代理的方法打上标记
    [TransactionFlag]
    void Todo1();

    //  这是不需要被代理的方法
    void Todo2();
}

// 服务的实现类,访问级别“程序集内部可见”
internal class MyInternalCustomService: ICustomService
{
    // 实现服务方法1
    public void Todo1()
    {
        //.....
    }

    // 实现服务方法2
    public void Todo1()
    {
        //.....
    }
}

重现

用例1:

  1. 通过构造函数获取 ICustomService 的实例
  2. 调用 ICustomService.Todo1()
  3. 结果:正确运行

用例2(导致了错误):

  1. 通过构造函数获取 ICustomService 的实例
  2. 调用 ICustomService.Todo2()
  3. 结果:无法访问方法,报错 System.MethodAccessException

问题

  1. 这是一个 bug 吗?
  2. 是我的用法不对吗?

当访问被代理的方法时,正确运行;
当访问未被代理的方法时,抛出错误,但如果我将实现类设置为 public,它又正确运行了。

目前我的方案:

// 为以Service结尾的服务启用拦截器
config.Interceptors.AddTyped<TransactionalInterceptorAttribute>(Predicates.ForService("*Service"));

// 修改拦截器逻辑
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class TransactionInterceptorAttribute : AbstractInterceptorAttribute
{
    public override async Task Invoke(AspectContext context, AspectDelegate next)
    {
        // 增加条件
        if (!CanApply(context))
        {
            await next.Invoke(context);
            return;
        }

        // 启用事务...
        await next(context);
        // 提交事务...
    }

    private bool CanApply(AspectContext context)
    {
        return context.ProxyMethod.CustomAttributes.Any(x => x.AttributeType == typeof(TransactionalAttribute)); ;
    }
}

从外部程序集调用internal 函数是会报System.MethodAccessException异常的。。打了拦截器不会报异常的原因是这个地方会生成一个public的代理函数...