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

Automapper派生类型属性未赋值问题排查求助

为什么AutoMapper映射后NumberOfLines属性变为默认值?

问题重现

我尝试将MyGenericResponseObject<OrderSummaryViewModel>类型的对象映射到MyResponseObject,预期两个对象的Items列表内容完全一致,但实际映射后NumberOfLines属性变成了默认值0。使用环境为.NET6及最新稳定版AutoMapper。

代码示例

using System;
using System.Collections.Generic;
using AutoMapper;

public class OrderViewModel {
    public Guid Id {get;set;}
}

public class OrderSummaryViewModel : OrderViewModel {
    public int NumberOfLines {get;set;}
}

public class MyGenericResponseObject<T> {
    public List<T> Items {get;set;} = new();
}

public class MyResponseObject : MyGenericResponseObject<OrderSummaryViewModel> {
}

public class Program
{
    public static void Main()
    {
        var e1 = new MyGenericResponseObject<OrderSummaryViewModel>
        {
            Items = new() {
                new() { Id = Guid.NewGuid(), NumberOfLines = 420 }
            }
        };

        var config = new MapperConfiguration(cfg =>
        {
            cfg.CreateMap<OrderSummaryViewModel, OrderViewModel>();
            cfg.CreateMap<OrderViewModel, OrderSummaryViewModel>();
            cfg.CreateMap<MyGenericResponseObject<OrderSummaryViewModel>, MyResponseObject>();
        });

        var mapper = config.CreateMapper();

        var e2 = mapper.Map<MyGenericResponseObject<OrderSummaryViewModel>, MyResponseObject>(e1);

        foreach (var i in e1.Items)
        {
            Console.WriteLine($"{i.Id} - {i.NumberOfLines}");
        }

        Console.WriteLine("------------------");

        foreach (var i in e2.Items)
        {
            Console.WriteLine($"{i.Id} - {i.NumberOfLines}");
        }
    }
}

实际输出

701cde6c-4751-4419-a2cb-1d347a3eb0e6 - 420
------------------
701cde6c-4751-4419-a2cb-1d347a3eb0e6 - 0

补充测试结果

配置1:移除OrderSummaryViewModel -> OrderViewModel映射

var config = new MapperConfiguration(cfg =>
{
    //cfg.CreateMap<OrderSummaryViewModel, OrderViewModel>();
    cfg.CreateMap<OrderViewModel, OrderSummaryViewModel>();
    cfg.CreateMap<MyGenericResponseObject<OrderSummaryViewModel>, MyResponseObject>();
});

输出:

1d31bf14-0bf2-4a10-8c4d-92efa9d6e131 - 420
------------------
1d31bf14-0bf2-4a10-8c4d-92efa9d6e131 - 0

配置2:移除OrderViewModel -> OrderSummaryViewModel映射

var config = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<OrderSummaryViewModel, OrderViewModel>();
    //cfg.CreateMap<OrderViewModel, OrderSummaryViewModel>();
    cfg.CreateMap<MyGenericResponseObject<OrderSummaryViewModel>, MyResponseObject>();
});

输出(报错):

Unhandled exception. System.ArgumentException: Expression of type 'OrderViewModel' cannot be used for parameter of type 'OrderSummaryViewModel' of method 'Void Add(OrderSummaryViewModel)' (Parameter 'arg0')
   at System.Dynamic.Utils.ExpressionUtils.ValidateOneArgument(MethodBase method, ExpressionType nodeKind, Expression arguments, ParameterInfo pi, String methodParamName, String argumentParamName, Int32 index)
   at System.Linq.Expressions.Expression.Call(Expression instance, MethodInfo method, Expression arg0)
   at System.Linq.Expressions.Expression.Call(Expression instance, MethodInfo method, IEnumerable`1 arguments)
   at AutoMapper.Internal.Mappers.CollectionMapper.<MapExpression>g__MapCollectionCore|2_1(Expression destExpression, <>c__DisplayClass2_0& )
   at AutoMapper.Internal.Mappers.CollectionMapper.MapExpression(IGlobalConfiguration configuration, ProfileMap profileMap, MemberMap memberMap, Expression sourceExpression, Expression destExpression)
   at AutoMapper.Execution.ExpressionBuilder.MapExpression(IGlobalConfiguration configuration, ProfileMap profileMap, TypePair typePair, Expression source, MemberMap memberMap, Expression destination)
   at AutoMapper.Execution.TypeMapPlanBuilder.MapMember(MemberMap memberMap, Expression destinationMemberValue, ParameterExpression resolvedValue)
   at AutoMapper.Execution.TypeMapPlanBuilder.CreatePropertyMapFunc(MemberMap memberMap, Expression destination, MemberInfo destinationMember)
   at AutoMapper.Execution.TypeMapPlanBuilder.CreateAssignmentFunc(Expression createDestination)
   at AutoMapper.Execution.TypeMapPlanBuilder.CreateMapperLambda()
   at AutoMapper.TypeMap.CreateMapperLambda(IGlobalConfiguration configuration)
   at AutoMapper.TypeMap.Seal(IGlobalConfiguration configuration)
   at AutoMapper.MapperConfiguration.<.ctor>g__Seal|20_0()
   at AutoMapper.MapperConfiguration..ctor(MapperConfigurationExpression configurationExpression)
   at AutoMapper.MapperConfiguration..ctor(Action`1 configure)
   at Program.Main()
Command terminated by signal 6

配置3:移除两个类型的双向映射

var config = new MapperConfiguration(cfg =>
{
    //cfg.CreateMap<OrderSummaryViewModel, OrderViewModel>();
    //cfg.CreateMap<OrderViewModel, OrderSummaryViewModel>();
    cfg.CreateMap<MyGenericResponseObject<OrderSummaryViewModel>, MyResponseObject>();
});

输出(符合预期):

d9e72e9e-9bc0-41fb-bf4d-33242dca5027 - 420
------------------
d9e72e9e-9bc0-41fb-bf4d-33242dca5027 - 420

原因分析

问题出在显式配置的OrderViewModel与OrderSummaryViewModel之间的双向映射:

  1. AutoMapper处理集合映射时,会检查元素类型的映射规则。虽然源和目标集合的元素类型都是OrderSummaryViewModel,但由于配置了该类型与父类OrderViewModel的映射,AutoMapper会错误触发类型转换逻辑。
  2. 具体流程为:将OrderSummaryViewModel先映射为OrderViewModel(丢失子类独有的NumberOfLines属性),再尝试映射回OrderSummaryViewModel,此时NumberOfLines会被初始化为默认值0。
  3. 移除这两个映射配置后,AutoMapper会使用默认的同类型映射逻辑,直接复制所有属性,结果符合预期。

解决方案

  • 如果不需要OrderViewModel与OrderSummaryViewModel之间的映射,直接移除这两个CreateMap配置即可。
  • 如果确实需要这两个类型的映射,需显式指定同类型的映射规则,确保AutoMapper优先使用同类型复制:
    cfg.CreateMap<OrderSummaryViewModel, OrderSummaryViewModel>().ReverseMap();
    
    或者在配置集合映射时明确指定使用同类型映射,避免触发父类子类的转换逻辑。

内容的提问来源于stack exchange,提问作者sommmen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 16:50:21