如何在Autofac的RegisterAssemblyTypes中按类名长度排序解决注入错误
问题背景
有两个以Service结尾的服务类:TurnoverGroupWebService和CashregisterTurnoverGroupWebService,本该分别注册到IReadTurnoverGroupWebService和IReadCashregisterTurnoverGroupWebService接口,但因为两者都以TurnoverGroupWebService结尾,导致CashregisterTurnoverGroupWebService被错误注入接口。当前注册代码如下:
public class AutofacHelper { public static void ScanAssembly(Assembly thisAssembly, ContainerBuilder builder) { var registerTypeStrings = GetRegisterTypeStrings(); foreach (var item in registerTypeStrings) { builder.RegisterAssemblyTypes(thisAssembly) .Where(t => t.Name.EndsWith(item)) .AsImplementedInterfaces() .SingleInstance(); } } }
你尝试通过OrderByDescending按类型名称长度排序来解决问题,但因RegisterAssemblyTypes返回的不是可枚举集合,导致编译报错:
builder.RegisterAssemblyTypes(thisAssembly) .OrderByDescending(t => t.Name.Length) .Where(t => t.Name.EndsWith(item)) .AsImplementedInterfaces() .SingleInstance();
解决方案
Autofac的RegisterAssemblyTypes返回的是IRegistrationBuilder而非可枚举集合,无法直接使用OrderByDescending。可以通过以下两种方式实现精准匹配:
方式一:先排序类型再逐个注册
先筛选出所有符合后缀条件的类型,按名称长度倒序排序后逐个注册,确保更长的类名优先匹配对应接口:
public class AutofacHelper { public static void ScanAssembly(Assembly thisAssembly, ContainerBuilder builder) { var registerTypeStrings = GetRegisterTypeStrings(); // 筛选所有符合后缀的类型并按名称长度倒序 var targetTypes = thisAssembly.GetTypes() .Where(t => registerTypeStrings.Any(s => t.Name.EndsWith(s))) .OrderByDescending(t => t.Name.Length) .ToList(); foreach (var type in targetTypes) { // 找到当前类型匹配的最长后缀,避免短后缀误匹配 var matchedSuffix = registerTypeStrings.OrderByDescending(s => s.Length) .First(s => type.Name.EndsWith(s)); builder.RegisterType(type) .AsImplementedInterfaces() .SingleInstance(); } } }
方式二:先排序后缀列表再过滤注册
对registerTypeStrings按长度倒序排列,优先处理更长的后缀,同时过滤已匹配过长后缀的类型,避免重复注册:
public class AutofacHelper { public static void ScanAssembly(Assembly thisAssembly, ContainerBuilder builder) { var registerTypeStrings = GetRegisterTypeStrings() .OrderByDescending(s => s.Length) .ToList(); foreach (var item in registerTypeStrings) { builder.RegisterAssemblyTypes(thisAssembly) .Where(t => t.Name.EndsWith(item) // 排除已匹配更长后缀的类型 && !registerTypeStrings.Any(s => s.Length > item.Length && t.Name.EndsWith(s))) .AsImplementedInterfaces() .SingleInstance(); } } }
原理说明
两种方式核心逻辑一致:优先匹配更长的后缀或类名,确保CashregisterTurnoverGroupWebService不会被TurnoverGroupWebService的短后缀规则误匹配,从而正确关联到IReadCashregisterTurnoverGroupWebService接口。
内容的提问来源于stack exchange,提问作者J. Rahmati

