如何用AutoMapper的IQueryable.ProjectTo实现字典<A,B>到<A,C>的投影?
如何让AutoMapper的
ProjectTo支持字典类型投影 问题背景
需要将IDictionary<Guid, From>映射到Dictionary<Guid, To>:
- 使用
IMapper.Map可以正常工作 - 使用
IQueryable.ProjectTo时提示缺少字典类型映射 - 添加
CreateMap<IDictionary<Guid, From>, IDictionary<Guid, To>>()后,所有测试因字典索引器参数错误失败 - 手动通过
ToDictionary实现的投影可以正常运行
原始测试代码
using System; using System.Collections.Generic; using System.Linq; using AutoMapper; using AutoMapper.QueryableExtensions; using Xunit; public class AutoMapperTests { private readonly IMapper mapper = new MapperConfiguration( (cfg) => { cfg.CreateMap<From, To>(); } ).CreateMapper(); [Fact] public void AutoMapperConfigurationIsValid() => mapper.ConfigurationProvider.AssertConfigurationIsValid(); [Fact] public void CanMapFromDictToDict() { var key = Guid.NewGuid(); var fromDict = CreateFromDict(key, key.ToString()); var toDict = new[] { fromDict } .AsEnumerable() .Select(it => mapper.Map<IDictionary<Guid, To>>(fromDict)) .Single(); Assert.Single(toDict); Assert.True(toDict.ContainsKey(key)); Assert.Equal(key.ToString(), toDict[key].Id); } [Fact] public void CanProjectFromDictToDict() { var key = Guid.NewGuid(); var fromDict = CreateFromDict(key, key.ToString()); var toDict = new[] { fromDict } .AsQueryable() .ProjectTo<IDictionary<Guid, To>>(mapper.ConfigurationProvider) .Single(); Assert.Single(toDict); Assert.True(toDict.ContainsKey(key)); Assert.Equal(key.ToString(), toDict[key].Id); } private IDictionary<Guid, From> CreateFromDict(Guid key, string id) => new Dictionary<Guid, From> { { key, new() { Id = id } } }; public class From { public required string Id { get; set; } } public class To { public required string Id { get; set; } } }
首次错误信息
System.InvalidOperationException : Missing map from System.Collections.Generic.IDictionary
2[System.Guid,AutoMapperTests+From] to System.Collections.Generic.IDictionary2[System.Guid,AutoMapperTests+To]. Create using CreateMap<IDictionary2, IDictionary2>.
添加字典映射后的错误信息
System.ArgumentException : Incorrect number of arguments supplied for call to method 'To get_Item(System.Guid)' (Parameter 'property')
解决方案
AutoMapper无法自动处理字典类型的投影表达式构建,需要为字典映射手动指定转换逻辑,同时兼容内存映射和ProjectTo两种场景:
private readonly IMapper mapper = new MapperConfiguration( (cfg) => { cfg.CreateMap<From, To>(); // 为字典类型自定义映射逻辑 cfg.CreateMap<IDictionary<Guid, From>, IDictionary<Guid, To>>() // 处理ProjectTo的投影表达式 .ProjectUsing(src => src.ToDictionary(kvp => kvp.Key, kvp => mapper.Map<To>(kvp.Value))) // 处理内存中的Map方法 .ConvertUsing((src, dest, ctx) => src.ToDictionary(kvp => kvp.Key, kvp => ctx.Mapper.Map<To>(kvp.Value))); } ).CreateMapper();
关键说明
ProjectUsing:为ProjectTo提供LINQ表达式树,明确告知AutoMapper如何将源字典投影为目标字典ConvertUsing:为内存中的Map方法提供转换逻辑,保持原有功能正常运行- 两种逻辑都基于已配置的
From到To的映射,无需重复定义字段映射规则
修改后所有测试均可通过,包括配置验证、内存映射和ProjectTo投影测试。
内容的提问来源于stack exchange,提问作者Felix ZY
相关产品推荐
相关产品推荐

