.NET中AutoMapper列表转扁平对象避免索引越界的方法
解决AutoMapper列表转扁平对象的索引越界问题
类定义
public class Container { public List<Product> Products; } public class Product { public string Name; public string Colour; } public class NewClass { public string Name1; public string Colour1; public string Name2; public string Colour2; public string Name3; public string Colour3; }
问题说明
需要将Container映射到NewClass,已知Products列表元素数量最多3个,但直接通过索引访问列表元素的AutoMapper配置会因为列表长度不足(比如只有1或2个元素)抛出索引越界异常。原错误配置存在语法错误和索引访问风险:
// 原错误配置示例 cfg.CreateMap<Container, NewClass>() .ForMember(d => d.Name1, opt => opt.MapFrom(src => src.Products[0].Name)) .ForMember(d => d.Name2, opt => opt.MapFrom(src => src.Products[1].Name)) .ForMember(d => d.Name3, opt => opt.MapFrom(src => src.Products[2].Name));
正确映射方法
方法1:用ElementAtOrDefault安全访问(最简洁)
利用LINQ的ElementAtOrDefault方法,当索引超出列表长度时返回null,结合空值运算符?.避免空引用,直接获取属性值:
cfg.CreateMap<Container, NewClass>() // 映射第一个产品 .ForMember(dest => dest.Name1, opt => opt.MapFrom(src => src.Products.ElementAtOrDefault(0)?.Name)) .ForMember(dest => dest.Colour1, opt => opt.MapFrom(src => src.Products.ElementAtOrDefault(0)?.Colour)) // 映射第二个产品 .ForMember(dest => dest.Name2, opt => opt.MapFrom(src => src.Products.ElementAtOrDefault(1)?.Name)) .ForMember(dest => dest.Colour2, opt => opt.MapFrom(src => src.Products.ElementAtOrDefault(1)?.Colour)) // 映射第三个产品 .ForMember(dest => dest.Name3, opt => opt.MapFrom(src => src.Products.ElementAtOrDefault(2)?.Name)) .ForMember(dest => dest.Colour3, opt => opt.MapFrom(src => src.Products.ElementAtOrDefault(2)?.Colour));
方法2:自定义值解析器(适合复杂逻辑复用)
如果需要处理默认值或更复杂的逻辑,可以自定义值解析器,复用索引判断逻辑:
// 自定义解析器:根据索引和属性选择器获取产品属性 public class ProductPropertyResolver : IValueResolver<Container, NewClass, string> { private readonly int _index; private readonly Func<Product, string> _propertySelector; public ProductPropertyResolver(int index, Func<Product, string> propertySelector) { _index = index; _propertySelector = propertySelector; } public string Resolve(Container source, NewClass destination, string destMember, ResolutionContext context) { if (source.Products == null || _index >= source.Products.Count) return null; // 也可以返回默认字符串,比如"" return _propertySelector(source.Products[_index]); } } // 配置映射 cfg.CreateMap<Container, NewClass>() .ForMember(d => d.Name1, opt => opt.MapFrom(new ProductPropertyResolver(0, p => p.Name))) .ForMember(d => d.Colour1, opt => opt.MapFrom(new ProductPropertyResolver(0, p => p.Colour))) .ForMember(d => d.Name2, opt => opt.MapFrom(new ProductPropertyResolver(1, p => p.Name))) .ForMember(d => d.Colour2, opt => opt.MapFrom(new ProductPropertyResolver(1, p => p.Colour))) .ForMember(d => d.Name3, opt => opt.MapFrom(new ProductPropertyResolver(2, p => p.Name))) .ForMember(d => d.Colour3, opt => opt.MapFrom(new ProductPropertyResolver(2, p => p.Colour)));
方法3:直接条件判断(直观易懂)
在映射表达式里直接判断列表长度,确保索引有效后再访问元素:
cfg.CreateMap<Container, NewClass>() .ForMember(d => d.Name1, opt => opt.MapFrom(src => src.Products != null && src.Products.Count > 0 ? src.Products[0].Name : null)) .ForMember(d => d.Colour1, opt => opt.MapFrom(src => src.Products != null && src.Products.Count > 0 ? src.Products[0].Colour : null)) .ForMember(d => d.Name2, opt => opt.MapFrom(src => src.Products != null && src.Products.Count > 1 ? src.Products[1].Name : null)) .ForMember(d => d.Colour2, opt => opt.MapFrom(src => src.Products != null && src.Products.Count > 1 ? src.Products[1].Colour : null)) .ForMember(d => d.Name3, opt => opt.MapFrom(src => src.Products != null && src.Products.Count > 2 ? src.Products[2].Name : null)) .ForMember(d => d.Colour3, opt => opt.MapFrom(src => src.Products != null && src.Products.Count > 2 ? src.Products[2].Colour : null));
内容的提问来源于stack exchange,提问作者jessye bottalico
相关产品推荐
相关产品推荐

