.NET 8反射DI遇泛型异步方法启动崩溃的解决方案
解决异步方法导致的依赖注入反射注册崩溃问题
问题背景
之前用下面这套基于反射的代码自动从程序集加载类型并注册到DI容器,省去了手动注册大量服务的麻烦,新增服务也不用改注册逻辑,一直运行正常:
using System.Reflection; namespace Server { public static class AddServices { public static IServiceCollection AddRepositories(this IServiceCollection services) { Assembly.Load("Services") .GetTypes() .Where(a => !a.IsAbstract && !a.IsInterface) .Select(a => new { assignedType = a, serviceTypes = a.GetInterfaces() }) .ToList() .ForEach(typeToRegister => { if (typeToRegister.serviceTypes.Length != 1) return; services.AddScoped(typeToRegister.serviceTypes[0], typeToRegister.assignedType); }); return services; } } }
直到添加了一个带泛型异步方法的Repository类后,程序启动直接崩溃:
using Dto.Interfaces; namespace Services { public interface IRepository { public Task<decimal> Calculate<T>(List<T> list) where T : ICommonProps; } public class Repository : IRepository { public async Task<decimal> Calculate<T>(List<T> list) where T : ICommonProps { await Task.Delay(100); foreach (var thing in list) { Console.WriteLine(thing); } return 0m; } } }
报错信息如下:
Cannot instantiate implementation type 'DotNetEightFeatureTesting.Server.Services.Repository+d__0`1[T]' for service type 'System.Runtime.CompilerServices.IAsyncStateMachine'
这个错误来自Task框架的内部实现,是编译器为异步方法自动生成的状态机类被误注册导致的。如果直接加!a.IsGenericType过滤泛型类型,虽然能解决问题,但会把合法的泛型服务也排除掉,不符合实际需求。
解决方案
修改反射筛选逻辑,排除编译器自动生成的类型,同时保留合法的泛型服务注册能力。只需要在Where条件里添加对CompilerGeneratedAttribute的判断即可:
using System.Reflection; using System.Runtime.CompilerServices; namespace Server { public static class AddServices { public static IServiceCollection AddRepositories(this IServiceCollection services) { Assembly.Load("Services") .GetTypes() .Where(a => !a.IsAbstract && !a.IsInterface && !a.GetCustomAttributes(typeof(CompilerGeneratedAttribute), inherit: true).Any()) .Select(a => new { assignedType = a, serviceTypes = a.GetInterfaces() }) .ToList() .ForEach(typeToRegister => { if (typeToRegister.serviceTypes.Length != 1) return; services.AddScoped(typeToRegister.serviceTypes[0], typeToRegister.assignedType); }); return services; } } }
修改说明
- 新增的
!a.GetCustomAttributes(typeof(CompilerGeneratedAttribute), inherit: true).Any()会过滤掉所有编译器自动生成的类型(包括异步方法的状态机类) - 没有添加
!a.IsGenericType的限制,保留了合法泛型服务的注册能力 - 原有逻辑中对“实现单个接口才注册”的规则保持不变,符合你的业务场景
这样修改后,既解决了启动崩溃的问题,又不会影响合法服务(包括泛型服务)的正常注册,两个API端点也能正常响应请求。
内容的提问来源于stack exchange,提问作者Nitrox Tank
相关产品推荐
相关产品推荐

