ASP.NET Core 5.0 MVC用Mailkit发邮件访问ProductType导航属性报NullReferenceException
异常触发的核心原因
- EF Core 默认不会自动填充未显式加载的导航属性
- 从前端通过
[Bind]模型绑定得到的sale实体,仅包含ProductTypeId标量外键值,对应的ProductType导航属性在实例化时为null - 执行
_context.Add(sale)和SaveChanges()时,EF Core 仅将当前传入的标量字段值写入数据库,不会主动关联查询填充导航属性,此时直接访问sale.ProductType.ProductTypeDesc必然触发空引用
- 从前端通过
- 代码存在两处会直接导致报错的问题
- 字段拼写错误:实体中定义的描述字段为
ProductTypeDesc,发送邮件的代码中写为ProductTypeDecs,即使导航属性正常加载,也会因拼写不匹配抛错 - 外键映射类型不匹配:
Sale实体中ProductTypeId定义为string类型,ProductType实体的主键ProductTypeId定义为int类型,两者类型不一致时EF Core 无法建立正确的外键关联映射,会进一步阻碍导航属性的正常加载
- 字段拼写错误:实体中定义的描述字段为
- 仅使用
sale.ProductTypeId可正常运行,是因为该值是模型绑定直接赋值的标量字段,不存在依赖数据库关联查询的逻辑。
可行解决方案
根据现有代码结构,可选择以下任意一种方式实现业务需求:
- 方案1:保存记录后显式查询关联产品类型(改动最小,兼容现有代码)
在SaveChanges()完成后,通过已获取的ProductTypeId值直接查询对应的产品类型记录,用于邮件内容拼接,代码示例:_context.Add(sale); _context.SaveChanges(); // 直接查询匹配的产品类型记录 var matchedProductType = _context.ProductType.Find(int.Parse(sale.ProductTypeId)); // 空值兜底,避免无效外键值导致的再次报错 string productTypeDesc = matchedProductType?.ProductTypeDesc ?? "未匹配到对应产品类型"; message.Body = new TextPart(TextFormat.Html) { Text = "<b>SaleId:</b> " + sale.SaleId + "<br/>" + "<b>ProductName:</b> " + sale.ProductName + "<br/>" + "<b>ProductType</b>: " + productTypeDesc }; - 方案2:通过EF Core 导航属性加载机制填充关联数据
如果需要直接使用sale.ProductType导航属性取值,可在保存记录后显式加载对应导航属性,代码示例:_context.Add(sale); _context.SaveChanges(); // 显式加载ProductType导航属性 _context.Entry(sale).Reference(s => s.ProductType).Load(); // 空值兜底 string productTypeDesc = sale.ProductType?.ProductTypeDesc ?? "未匹配到对应产品类型"; - 方案3:修正实体映射关系
首先统一Sale和ProductType两个实体的主键/外键类型,将Sale中的ProductTypeId调整为与ProductType主键一致的int类型,从根源上让EF Core 能正确建立外键关联,后续涉及导航属性的查询、加载逻辑才能正常生效。
代码优化提示
- 邮件发送逻辑建议从控制器的Create方法中解耦为独立服务,避免控制器同时承担数据持久化、业务校验、第三方服务调用的多类职责
- 当前代码中邮件发送逻辑嵌在数据库操作的事务范围内,若邮件服务器连接超时,会导致整个销售创建流程异常,可将邮件发送逻辑调整为数据库事务提交后执行,或引入异步队列解耦
- 所有可能为null的引用访问,统一使用
?.运算符做空值兜底,避免无效数据触发运行时异常 - 修正代码中的字段拼写错误,将
ProductTypeDecs调整为实体定义的ProductTypeDesc
内容的提问来源于stack exchange,提问作者paris_n
相关产品推荐
相关产品推荐

