如何从程序集中干净扫描AutoMapper Profiles?规避内置Profile异常
你的临时方案确实能解决问题,但靠类型全名前缀过滤确实有点“hack”——毕竟如果AutoMapper以后调整命名空间,或者有第三方库的Profile也用类似前缀,就容易出问题。这里有几个更稳健优雅的实现方式:
方法一:从程序集层面过滤(最直接)
我们可以直接排除AutoMapper官方程序集,只加载你自己项目或依赖的非AutoMapper程序集里的Profile类型,从根源上避免拿到框架内置的Profile:
public AutomapperTypeAdapterFactory() { var targetAssemblies = AppDomain.CurrentDomain.GetAssemblies() // 排除AutoMapper核心程序集,精准过滤根源 .Where(a => !a.FullName.StartsWith("AutoMapper,")); var profiles = targetAssemblies .SelectMany(a => a.GetTypes()) // 补充过滤:只考虑非抽象的类类型 .Where(t => t.IsClass && !t.IsAbstract && t.BaseType == typeof(Profile)); Mapper.Initialize(cfg => { foreach (var profile in profiles) { cfg.AddProfile((Profile)Activator.CreateInstance(profile)); } }); }
这个方法的优势是精准度高,不会误杀第三方库的自定义Profile,只要它们不在AutoMapper的程序集里就会被正常加载。
方法二:筛选可安全实例化的类型(最通用)
AutoMapper内置的NamedProfile这类类型之所以会报错,核心原因是没有公共无参构造函数。我们可以提前筛选出能安全实例化的Profile,同时还能避免自己写的Profile因为构造函数问题报错:
public AutomapperTypeAdapterFactory() { var profiles = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(a => a.GetTypes()) .Where(t => t.IsClass && !t.IsAbstract && t.BaseType == typeof(Profile) // 检查是否存在公共无参构造函数,从根源避免实例化异常 && t.GetConstructor(Type.EmptyTypes) != null); Mapper.Initialize(cfg => { foreach (var profile in profiles) { cfg.AddProfile((Profile)Activator.CreateInstance(profile)); } }); }
这个方法适用性更广,不管是框架内置的还是自己写的不符合要求的Profile,都会被提前过滤掉,避免运行时抛出异常。
方法三:用自定义特性标记(最灵活)
如果你的项目里有大量自定义Profile,还可以给它们标记一个专属特性,这样筛选时只加载带有该特性的类型,完全不受命名空间或程序集的限制:
首先定义一个自定义特性:
[AttributeUsage(AttributeTargets.Class)] public class CustomAutoMapperProfileAttribute : Attribute { }
然后给你的自定义Profile加上这个标记:
[CustomAutoMapperProfile] public class UserProfile : Profile { // 你的映射配置... }
最后在扫描时精准过滤:
public AutomapperTypeAdapterFactory() { var profiles = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(a => a.GetTypes()) .Where(t => t.IsClass && !t.IsAbstract && t.BaseType == typeof(Profile) // 只加载带有自定义特性的Profile && t.GetCustomAttribute<CustomAutoMapperProfileAttribute>() != null); Mapper.Initialize(cfg => { foreach (var profile in profiles) { cfg.AddProfile((Profile)Activator.CreateInstance(profile)); } }); }
这种方式灵活性拉满,你可以完全掌控哪些Profile被加载,哪怕以后项目结构调整也不会受影响。
小补充:原来代码里的as Profile可以改成强转(Profile)Activator.CreateInstance(profile),因为我们已经通过过滤确保了类型是Profile的子类,强转更安全,还能在编译时避免潜在的类型转换问题。
内容的提问来源于stack exchange,提问作者xariez

