dotnetcore / AspectCore-Framework

AspectCore is an AOP-based cross platform framework for .NET Standard.
MIT License
1.69k stars 324 forks source link

Bug:使用拦截器的时候从容器获取对象时调用的构造方法错误 #313

Closed MateralCMX closed 1 year ago

MateralCMX commented 1 year ago

我有这样两个接口

    public interface IInteriorService
    {
        void SayHello();
    }
    public interface IExternalService
    {
        void SayHello();
    }

有两个类分别实现了他们

    public class InteriorServiceImpl : IInteriorService
    {
        public void SayHello() => Console.WriteLine("Hello Interior!");
    }
    public class ExternalServiceImpl : IExternalService
    {
        private readonly IInteriorService? _service1;
        public ExternalServiceImpl()
        {
            Console.WriteLine("ExternalServiceImpl()");
        }
        public ExternalServiceImpl(IInteriorService service1) : this()
        {
            _service1 = service1;
            Console.WriteLine("ExternalServiceImpl(IParentService)");
        }
        public void SayHello()
        {
            Console.WriteLine("Hello External!");
            _service1?.SayHello();
        }
    }

并实现了一个拦截器

    public class CustomInterceptor : AbstractInterceptor
    {
        public override async Task Invoke(AspectContext context, AspectDelegate next) => await context.Invoke(next);
    }

这是主要方法

    public class Program
    {
        public static void Main()
        {
            IServiceCollection services = new ServiceCollection();
            services.AddTransient<IInteriorService, InteriorServiceImpl>();
            services.AddTransient<IExternalService, ExternalServiceImpl>();
            services.ConfigureDynamicProxy(option =>
            {
                option.Interceptors.AddTyped<CustomInterceptor>();
            });
            IServiceProvider serviceProvider = services.BuildDynamicProxyProvider();
            IExternalService service2 = serviceProvider.GetRequiredService<IExternalService>();
            service2.SayHello();
        }
    }

期望输出为:

ExternalServiceImpl()
ExternalServiceImpl(IParentService)
Hello External!
Hello Interior!

实际输出为:

ExternalServiceImpl()
Hello External!

经过调试发现,在使用拦截器后(不论是全局方式还是Attribute方式),从容器获取接口实现类时调用的构造函数不是参数最多的构造函数(这只是表象),在该示例中,容器调用了方法ExternalServiceImpl()而非ExternalServiceImpl(IParentService)方法,我还发现调用的构造函数与代码文件中的位置有关 将类ExternalServiceImpl调整为如下代码后(交换了构造函数的位置),获得了期望输出

    public class ExternalServiceImpl : IExternalService
    {
        private readonly IInteriorService? _service1;
        public ExternalServiceImpl(IInteriorService service1) : this()
        {
            _service1 = service1;
            Console.WriteLine("ExternalServiceImpl(IParentService)");
        }
        public ExternalServiceImpl()
        {
            Console.WriteLine("ExternalServiceImpl()");
        }
        public void SayHello()
        {
            Console.WriteLine("Hello External!");
            _service1?.SayHello();
        }
    }

示例地址AspectCoreDemo

nivalxer commented 1 year ago

这个问题上是runtime的一个bug,在.net8里面被修复了 https://github.com/dotnet/runtime/issues/46132 临时修复方案就是按提供的示例一样,把需要注入的构造函数放前面

liuhaoyang commented 1 year ago

👍🏻

MateralCMX commented 1 year ago

这个问题上是runtime的一个bug,在.net8里面被修复了 dotnet/runtime#46132 临时修复方案就是按提供的示例一样,把需要注入的构造函数放前面

经过验证确实是这个Bug导致的