You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何配置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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 03:33:18