如何配置AutoMapper实现A到INC<B>的通用双向映射?
实现AutoMapper从任意A到INC的通用双向映射
要搞定MvvmCross中Model到ViewModel的INC<T>类型通用双向映射,不用逐个类型手动写配置,咱们可以通过AutoMapper的自定义映射规则来实现,完全覆盖你提的所有要求:目标INC<>为null时创建实例,非null则复用;映射时把Value设为A到B的映射结果;源为null时将INC<T>.Value设为默认值。
第一步:明确基础类型定义
先把你给出的基础类和接口补全(确保AutoMapper能正常访问):
namespace MvvmCross.Binding { public interface INC<T> { T Value { get; set; } } } // 补充访问修饰符,确保AutoMapper可访问 public class Model { public string Name { get; set; } public DateTime When { get; set; } public OtherModel Data { get; set; } } public class OtherModel { public string Detail { get; set; } } public class ViewModel { public INC<string> Name { get; set; } public INC<string> When { get; set; } public INC<OtherViewModel> Data { get; set; } } public class OtherViewModel { public string Detail { get; set; } } // 实现INC<T>的具体实例类(接口无法直接实例化,可替换为你项目中的实际实现) public class NC<T> : INC<T> { public T Value { get; set; } }
第二步:配置通用的INC映射规则
接下来给AutoMapper配置通用映射规则,所有INC<T>类型都能自动套用:
var configuration = new MapperConfiguration(cfg => { // 正向映射:任意类型A → INC<B> cfg.CreateMap<object, INC<object>>() .ConvertUsing((source, destination, context) => { // 获取目标INC的泛型参数类型B var targetType = destination?.GetType() ?? context.DestinationType; var valueType = targetType.GetGenericArguments()[0]; // 目标为null则创建实例,非null则复用 var incInstance = destination ?? Activator.CreateInstance(targetType) as INC<object>; if (source == null) { // 源为null时,设置Value为对应类型的默认值 incInstance.Value = Activator.CreateInstance(valueType); return incInstance; } // 动态映射A到B,赋值给INC的Value属性 var mappedValue = context.Mapper.Map(source, source.GetType(), valueType); incInstance.Value = mappedValue; return incInstance; }) .IncludeAllDerived(); // 让规则适用于所有INC<T>的派生类型 // 反向映射:INC<B> → 任意类型A cfg.CreateMap<INC<object>, object>() .ConvertUsing((source, destination, context) => { if (source == null || source.Value == null) { return destination ?? Activator.CreateInstance(context.DestinationType); } return context.Mapper.Map(source.Value, source.Value.GetType(), context.DestinationType); }) .IncludeAllDerived(); // 配置子类型的常规映射 cfg.CreateMap<Model, ViewModel>(); cfg.CreateMap<OtherModel, OtherViewModel>(); // 反向映射同理 cfg.CreateMap<ViewModel, Model>(); cfg.CreateMap<OtherViewModel, OtherModel>(); });
第三步:关键规则说明
咱们的配置完全满足你的核心需求:
- 目标INC<>为null则创建实例:通过
destination ?? Activator.CreateInstance(targetType)判断,目标为null时用反射生成INC<T>的实现类实例。 - 非null则不重建:直接复用传入的目标实例,仅更新
Value属性,避免不必要的对象创建。 - 映射Value为Mapper.Map<A,B>(source):通过
context.Mapper.Map动态完成源类型到目标泛型参数类型的映射,保证映射逻辑和其他类型一致。 - 源为null时INC
.Value设为默认值 :源为null时,用Activator.CreateInstance生成对应类型的默认实例(值类型为内置默认值,引用类型为null)。
第四步:测试验证
你可以用以下代码验证效果:
var mapper = configuration.CreateMapper(); // 测试正向映射:Model → ViewModel var model = new Model { Name = "Test Name", When = DateTime.Now, Data = new OtherModel { Detail = "Test Detail" } }; var viewModel = mapper.Map<ViewModel>(model); Console.WriteLine(viewModel.Name.Value); // 输出 "Test Name" Console.WriteLine(viewModel.When.Value); // 输出当前时间 Console.WriteLine(viewModel.Data.Value.Detail); // 输出 "Test Detail" // 测试源为null的情况 Model nullModel = null; var nullViewModel = mapper.Map<ViewModel>(nullModel); Console.WriteLine(nullViewModel.Name.Value); // 输出空字符串(string默认值) Console.WriteLine(nullViewModel.When.Value); // 输出DateTime.MinValue Console.WriteLine(nullViewModel.Data.Value); // 输出null(引用类型默认值) // 测试复用已有的INC实例 var existingViewModel = new ViewModel { Name = new NC<string> { Value = "Existing Name" } }; mapper.Map(model, existingViewModel); Console.WriteLine(existingViewModel.Name.Value); // 输出 "Test Name"(复用原实例,仅更新Value)
内容的提问来源于stack exchange,提问作者Flavourous
相关产品推荐
相关产品推荐

