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

AutoMapper映射含SortedSet构造参数的Parent对象失败求助

AutoMapper映射SortedSet到Parent构造函数失败问题分析与解决

问题背景

使用AutoMapper v12.0.1定义如下类与映射配置:

public record Parent(SortedSet<Child> Children);

public record Child(int Index);

public class ChildComparer : IComparer<Child>
{
    public int Compare(Child x, Child y)
    {
        return x.Index.CompareTo(y.Index);
    }
}

var mapper = new MapperConfiguration(
    cfg =>
    {
        cfg.CreateMap<int, Child>()
            .ForCtorParam("Index", opt => opt.MapFrom(src => src));
        
        cfg.CreateMap<IEnumerable<int>, Parent>()           
            .ForCtorParam("Children", opt => opt.MapFrom((src, ctx) =>
                new SortedSet<Child>(
                    ctx.Mapper.Map<List<Child>>(src.ToList()),
                    new ChildComparer())));
    })
    .CreateMapper();

以下操作均可正常执行:

var ints = new List<int> { 1, 2, 3 };
var res = mapper.Map<Child>(2);

var res2 = mapper.Map<List<Child>>(ints);

var resSet = new SortedSet<Child>(
    mapper.Map<List<Child>>(ints),
    new ChildComparer());

但执行映射生成Parent对象时失败:

var result = mapper.Map<Parent>(ints); 
// System.ArgumentException: At least one object must implement IComparable.

完整错误信息:

Unhandled exception. AutoMapper.AutoMapperMappingException: Error mapping types.

Mapping types:
IEnumerable`1 -> Parent
System.Collections.Generic.IEnumerable`1[[System.Int32, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]] -> Program+Parent

Type Map configuration:
IEnumerable`1 -> Parent
System.Collections.Generic.IEnumerable`1[[System.Int32, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]] -> Program+Parent

Destination Member:
Void .ctor(System.Collections.Generic.SortedSet`1[Program+Child]), parameter Children

 ---> System.ArgumentException: At least one object must implement IComparable.
   at System.Collections.Comparer.Compare(Object a, Object b)
   at System.Collections.Generic.SortedSet`1.AddIfNotPresent(T item)
   at System.Collections.Generic.SortedSet`1.System.Collections.Generic.ICollection<T>.Add(T item)
   at lambda_method3(Closure, Object, Parent, ResolutionContext)
   --- End of inner exception stack trace ---
   at lambda_method3(Closure, Object, Parent, ResolutionContext)
   at Program.Main()
Command terminated by signal 6

需求:找出问题原因,且尽可能继续使用.ForCtorParam方法和自定义Comparer类。

问题原因

AutoMapper对集合类型的构造函数参数存在默认行为:即使你通过ForCtorParam直接构造好了SortedSet<Child>实例,它仍会将该实例视为空目标集合,尝试把源IEnumerable<int>的元素逐个映射后添加进去。此时添加元素时,AutoMapper并未使用你传入的自定义ChildComparer,而是默认启用Comparer<Child>.Default——但Child类型未实现IComparable<Child>接口,因此触发了At least one object must implement IComparable错误。

解决方案

要保留.ForCtorParam和自定义Comparer,只需在配置中告诉AutoMapper不要对该构造函数参数执行自动集合填充操作,添加opt.DoNotValidate()即可:

var mapper = new MapperConfiguration(
    cfg =>
    {
        cfg.CreateMap<int, Child>()
            .ForCtorParam("Index", opt => opt.MapFrom(src => src));
        
        cfg.CreateMap<IEnumerable<int>, Parent>()           
            .ForCtorParam("Children", opt => 
            {
                opt.MapFrom((src, ctx) =>
                    new SortedSet<Child>(
                        ctx.Mapper.Map<List<Child>>(src.ToList()),
                        new ChildComparer()));
                // 禁用AutoMapper对该参数的集合验证与自动填充
                opt.DoNotValidate();
            });
    })
    .CreateMapper();

备选方案(不推荐,仅作参考)

如果允许修改Child类型,也可以让其实现IComparable<Child>接口,这样即使AutoMapper使用默认比较器也能正常工作:

public record Child(int Index) : IComparable<Child>
{
    public int CompareTo(Child? other)
    {
        return other is null ? 1 : Index.CompareTo(other.Index);
    }
}

但此方案会让Child自带排序逻辑,与自定义ChildComparer的设计初衷冲突,因此优先推荐第一种解决方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 02:44:56