如何解决Automapper与EF Core中的‘Nullable object must have a value’错误
解决Automapper映射时
Nullable object must have a value错误 问题场景
开发Blazor Server应用时,为OrderDetail实体新增了可空的ShippingWeek字段,但使用Automapper的ProjectTo方法将Order实体映射到OrderDto时,抛出System.InvalidOperationException: Nullable object must have a value错误。
相关代码片段
OnInitializedAsync中的映射代码:
IQueryable<OrderDto?> _ordersDto = Mapper.ProjectTo<OrderDto>(_order);
Order与OrderDto的Automapper配置:
CreateMap<Order, OrderDto>() .ForMember(dest => dest.OrderDetailsDto, opt => opt.MapFrom(src => src.OrderDetails)) .ForMember(dest => dest.Customer, opt => opt.MapFrom(src => src.Customer)) .ForAllMembers(opts => { opts.Condition((src, dest, srcMember) => srcMember != null); });
简化后的实体与DTO类:
public class Order { public int Id { get; set; } public List<OrderDetail> OrderDetails { get; set; } } public class OrderDto { public int Id { get; set; } public List<OrderDetailDto> OrderDetailsDto { get; set; } } public class OrderDetail { public int Id { get; set; } public string? Currency { get; set; } public int OrderId { get; set; } public Order Order { get; set; } public int ShippingWeek { get; set; } // 声明为非可空,但实际数据库字段为可空 } public class OrderDetailDto { public int Id { get; set; } public string? Currency { get; set; } public int OrderId { get; set; } public OrderDto OrderDto { get; set; } public int ShippingWeek { get; set; } // 对应实体的非可空类型 }
OrderDetail与OrderDetailDto的映射配置:
CreateMap<OrderDetail, OrderDetailDto>() .ForMember(dest => dest.OrderDto, opt => opt.MapFrom(src => src.Order)) .ForMember(dest => dest.VendorDto, opt => opt.MapFrom(src => src.Vendor)) .ForMember(dest => dest.ShippingWeek, opt => opt.MapFrom(src => src.ShippingWeek)) .ForAllMembers(opts => { opts.Condition((src, dest, srcMember) => srcMember != null); });
问题根源
- 类型不匹配:
OrderDetail实体的ShippingWeek声明为非可空int,但实际数据库字段是可空的,EF Core查询时会将数据库的null值尝试赋值给非可空int,引发错误。 - Automapper条件限制:
ForAllMembers中的srcMember != null条件对非可空值类型(如int)永远为true,但当数据库返回null时,Automapper无法将null映射到非可空int类型的DTO字段。
解决方案
1. 修正实体与DTO的字段类型
将OrderDetail和OrderDetailDto中的ShippingWeek改为可空int,与数据库字段的可空性保持一致:
public class OrderDetail { // 其他字段不变 public int? ShippingWeek { get; set; } // 修改为可空类型 } public class OrderDetailDto { // 其他字段不变 public int? ShippingWeek { get; set; } // 同步改为可空类型 }
2. 调整Automapper映射条件
针对可空值类型调整ForAllMembers的条件,允许null值映射:
CreateMap<OrderDetail, OrderDetailDto>() .ForMember(dest => dest.OrderDto, opt => opt.MapFrom(src => src.Order)) .ForMember(dest => dest.VendorDto, opt => opt.MapFrom(src => src.Vendor)) .ForAllMembers(opts => { // 允许可空值类型的null被映射,同时过滤引用类型的null opts.Condition((src, dest, srcMember) => srcMember != null || (srcMember is ValueType && Nullable.GetUnderlyingType(srcMember.GetType()) != null)); });
如果仅需跳过引用类型的null,可简化条件:
.ForAllMembers(opts => { opts.Condition((src, dest, srcMember) => srcMember == null ? false : !srcMember.GetType().IsValueType); });
3. 移除不必要的单独字段映射
由于ShippingWeek字段名在实体和DTO中一致,可移除单独的ForMember配置,Automapper会自动匹配映射。
验证
修改完成后,重新运行ProjectTo映射,即可正常处理ShippingWeek为null的场景,不再抛出错误。
内容的提问来源于stack exchange,提问作者raysefo
相关产品推荐
相关产品推荐

