如何在C#中利用反射实现多库同结构模型的自动映射?
自动生成Mapster跨类库模型映射方案
核心思路
利用反射遍历目标命名空间下的所有模型类,根据类名匹配不同类库中的对应模型,再针对构造函数或属性赋值的场景自动生成Mapster映射规则,彻底替代手动维护。
实现步骤与代码示例
1. 加载程序集并筛选模型类型
先通过反射加载三个类库的程序集,提取出对应命名空间下的所有模型类:
// 加载三个类库的程序集,替换为实际的程序集名称 var assemblyA = Assembly.Load("LibraryA"); var assemblyB = Assembly.Load("LibraryB"); var assemblyC = Assembly.Load("LibraryC"); // 获取各命名空间下的模型类型,按类名存入字典方便匹配 var modelsA = assemblyA.GetTypes() .Where(t => t.Namespace == "LibraryA.Models" && t.IsClass && !t.IsAbstract) .ToDictionary(t => t.Name); var modelsB = assemblyB.GetTypes() .Where(t => t.Namespace == "LibraryB.Models" && t.IsClass && !t.IsAbstract) .ToDictionary(t => t.Name); var modelsC = assemblyC.GetTypes() .Where(t => t.Namespace == "LibraryC.Models" && t.IsClass && !t.IsAbstract) .ToDictionary(t => t.Name);
2. 通用映射配置方法
编写通用方法自动为匹配的模型对创建映射,优先使用构造函数初始化(参数与源属性匹配时),否则用属性赋值:
public static void AutoConfigureMapsterMappings() { var config = TypeAdapterConfig.GlobalSettings; // 遍历LibraryA的模型,配置到LibraryB、LibraryC的映射 foreach (var (modelName, sourceType) in modelsA) { if (modelsB.TryGetValue(modelName, out var targetTypeB)) { ConfigureTypeMapping(config, sourceType, targetTypeB); } if (modelsC.TryGetValue(modelName, out var targetTypeC)) { ConfigureTypeMapping(config, sourceType, targetTypeC); } } // 按需添加其他方向的映射(如B→A、C→B等) } private static void ConfigureTypeMapping(TypeAdapterConfig config, Type sourceType, Type targetType) { // 优先查找匹配的构造函数:参数名、类型与源属性一致 var matchedCtor = FindMatchingConstructor(sourceType, targetType); if (matchedCtor != null) { var mappingConfig = config.NewConfig(sourceType, targetType); var mapWithMethod = typeof(TypeAdapterConfig<,>) .MakeGenericType(sourceType, targetType) .GetMethod("MapWith", new[] { typeof(Expression<Func<,>>).MakeGenericType(sourceType, targetType) }); var initExpr = CreateConstructorInitExpression(sourceType, targetType, matchedCtor); mapWithMethod.Invoke(mappingConfig, new[] { initExpr }); return; } // 无匹配构造函数时,直接配置属性映射 var propertyMapping = config.NewConfig(sourceType, targetType); var sourceProps = sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance); foreach (var srcProp in sourceProps) { var targetProp = targetType.GetProperty(srcProp.Name, BindingFlags.Public | BindingFlags.Instance); if (targetProp != null && targetProp.CanWrite && srcProp.PropertyType == targetProp.PropertyType) { propertyMapping.Map(targetProp.Name, srcProp.Name); } } } private static ConstructorInfo FindMatchingConstructor(Type sourceType, Type targetType) { var sourcePropsDict = sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance) .ToDictionary(p => p.Name); foreach (var ctor in targetType.GetConstructors(BindingFlags.Public | BindingFlags.Instance)) { var parameters = ctor.GetParameters(); // 验证所有构造参数都能在源类型找到同名同类型属性 if (parameters.All(p => sourcePropsDict.TryGetValue(p.Name, out var prop) && prop.PropertyType == p.ParameterType)) { return ctor; } } return null; } private static object CreateConstructorInitExpression(Type sourceType, Type targetType, ConstructorInfo constructor) { var sourceParam = Expression.Parameter(sourceType, "src"); var ctorParams = constructor.GetParameters() .Select(p => Expression.Property(sourceParam, p.Name)) .ToArray(); var newExpr = Expression.New(constructor, ctorParams); var lambdaType = typeof(Func<,>).MakeGenericType(sourceType, targetType); return Expression.Lambda(lambdaType, newExpr, sourceParam); }
3. 使用方式
在程序启动阶段调用AutoConfigureMapsterMappings(),即可完成所有模型的自动映射配置,之后正常使用Adapt方法转换:
// 示例:LibraryA模型转LibraryB模型 var libraryBObj = libraryAObj.Adapt<LibraryB.Models.AnObject>();
扩展说明
- 如果模型包含嵌套类,可在
ConfigureTypeMapping中添加递归逻辑,自动处理嵌套模型的映射。 - 可根据需求调整构造函数匹配规则(如忽略大小写),或添加自定义属性匹配逻辑。
- 若程序集加载失败,可改用
Assembly.LoadFrom指定程序集文件路径。
内容的提问来源于stack exchange,提问作者Kevin
相关产品推荐
相关产品推荐

