AutoMapper映射派生类OperationalAlert时字段为null致插入失败求助
问题:插入OperationalAlert时Content字段为null导致数据库报错
问题现象
插入Alert派生类型OperationalAlert实例时,实体的Content等字段为null,触发数据库"Column 'Content' cannot be null"错误。已确认DTO字段已填充,AutoMapper也配置了映射,但插入失败。
相关代码
创建警报方法
public async Task<Alert> CreateAlertAsync(AlertDTO alertDto) { if (alertDto == null) { throw new ArgumentNullException(nameof(alertDto), "Alert DTO cannot be null."); } Alert alert = alertDto.AlertTypeId switch { 1 => _mapper.Map<AlertInformational>(alertDto) as Alert, 2 => _mapper.Map<AlertIncident>(alertDto) as Alert, 3 => _mapper.Map<OperationalAlert>(alertDto) as Alert, 4 => _mapper.Map<OperationalAlert>(alertDto) as Alert, _ => throw new ArgumentOutOfRangeException(nameof(alertDto.AlertTypesId), "Invalid alert type"), }; var addedAlertType = await _alertRepository.AddAsync(alert); return addedAlertType; }
实体类定义
public class Alert { public int Id { get; set; } public int AlertTypeId{ get; set; } public int CreatorId { get; set; } public string Name { get; set; } public string Title { get; set; } public int TemplateRefId { get; set; } public int DepartmentId { get; set; } public bool IsProcessChanged { get; set; } public bool? IsPermanent { get; set; } public virtual Department? Department { get; set; } public virtual User? Creator { get; set; } public virtual AlertType? Type { get; set; } public virtual Template? Template { get; set; } public int ChangeDetailId { get; set; } public virtual ChangeDetail? ChangeDetail { get; set; } public virtual ICollection<Recipient>? AlertRecipients { get; set; } public virtual ICollection<Attachment>? AlertAttachments { get; set; } public virtual ICollection<Update>? AlertUpdates { get; set; } public virtual ICollection<CustomLog>? LogEntries { get; set; } public virtual ICollection<OperationDetail>? OperationDetails { get; set; } } public class OperationalAlert : Alert { public AlertState State { get; set; } public UrgencyLevel Urgency { get; set; } public bool IsEnabled { get; set; } public DateTime CreatedOn { get; set; } public string Content { get; set; } public DateTime ExpiresOn { get; set; } public string Location { get; set; } public string MessageContent { get; set; } public string Description { get; set; } public DateTime OccurrenceTime { get; set; } public string Method { get; set; } public string Reason { get; set; } public virtual ICollection<OperationalAlertRevision>? Revisions { get; set; } } public enum AlertState { Active, Inactive, Expired, Pending, Resolved } public enum UrgencyLevel { Low, Medium, High, Critical }
DTO定义
using System.Collections.Generic; namespace YourNamespace.Alerting.Domain.Model.Dto { public class AlertDTO { public int Id { get; set; } public int AlertTypeId { get; set; } public int CreatorId { get; set; } public string UserName { get; set; } public string Title { get; set; } public string Content { get; set; } public int TemplateRefId { get; set; } public int DepartmentId { get; set; } public bool IsProcessChanged { get; set; } public bool? IsPermanent { get; set; } public virtual ICollection<RecipientDto>? Recipients { get; set; } public virtual ICollection<UpdateDto>? Updates { get; set; } public virtual ICollection<AttachmentDto>? Attachments { get; set; } public virtual ICollection<OperationDto>? Operations { get; set; } public virtual ICollection<LogDto>? Logs { get; set; } public int ChangeDetailId { get; set; } } public class OperationalAlertDto : AlertDTO { public int Id { get; set; } public string Title { get; set; } public AlertState State { get; set; } public UrgencyLevel Urgency { get; set; } public bool IsEnabled { get; set; } public string Content { get; set; } public DateTime CreatedOn { get; set; } public DateTime ExpiresOn { get; set; } public string Description { get; set; } public DateTime OccurrenceTime { get; set; } public string Method { get; set; } public string Reason { get; set; } public string Location { get; set; } public string MessageContent { get; set; } public List<OperationalAlertRevisionDto>? Revisions { get; set; } } }
AutoMapper配置
public AutoMapperProfile() { CreateMap<Department, DepartmentDTO>().ReverseMap(); CreateMap<TemplateType, TemplateTypeDTO>().ReverseMap(); CreateMap<Template, TemplateDTO>().ReverseMap(); CreateMap<User, UserDTO>(); CreateMap<OperationalAlertDto, OperationalAlert>() .IncludeBase<AlertDTO, Alert>(); CreateMap<AlertDTO, AlertInformational>(); CreateMap<AlertDTO, AlertIncident>(); CreateMap<AlertDTO, OperationalAlert>().ReverseMap(); CreateMap<OperationalAlertDto, OperationalAlert>().ReverseMap(); CreateMap<OperationalAlertDto, OperationalAlert>().ReverseMap(); CreateMap<Alert, AlertDTO>() .ForMember(dest => dest.UserName, opt => opt.MapFrom(src => src.user != null ? src.user.UserName : null)) .Include<OperationalAlert, OperationalAlertDto>() .ReverseMap(); CreateMap<AlertRecipient, AlertRecipientDto>().ReverseMap(); CreateMap<AlertUpdate, AlertUpdateDto>().ReverseMap(); }
错误日志
Microsoft.EntityFrameworkCore.Update[10000] An exception occurred in the database while saving changes for context type 'alertapp_Alerting.Persistence.Context.alertappAlertingDbContext'. Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while saving the entity changes. See the inner exception for details. ---> MySqlConnector.MySqlException (0x80004005): Column 'Content' cannot be null at MySqlConnector.Core.ServerSession.ReceiveReplyAsync(IOBehavior ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/Core/ServerSession.cs:line 894 ...
解决方案
1. 修正映射逻辑,使用正确的DTO类型映射
当前方法用AlertDTO直接映射到OperationalAlert,若实际传入的是OperationalAlertDto,这种方式无法映射子类DTO的额外字段,甚至会导致基类字段映射异常。修改CreateAlertAsync方法的映射逻辑:
public async Task<Alert> CreateAlertAsync(AlertDTO alertDto) { if (alertDto == null) { throw new ArgumentNullException(nameof(alertDto), "Alert DTO cannot be null."); } Alert alert = alertDto.AlertTypeId switch { 1 => _mapper.Map<AlertInformational>(alertDto), 2 => _mapper.Map<AlertIncident>(alertDto), 3 or 4 => _mapper.Map<OperationalAlert>(alertDto as OperationalAlertDto ?? _mapper.Map<OperationalAlertDto>(alertDto)), _ => throw new ArgumentOutOfRangeException(nameof(alertDto.AlertTypeId), "无效的警报类型"), }; var addedAlertType = await _alertRepository.AddAsync(alert); return addedAlertType; }
2. 清理并优化AutoMapper配置
移除重复的映射规则,确保继承关系的映射正确:
public AutoMapperProfile() { // 基础实体映射 CreateMap<Department, DepartmentDTO>().ReverseMap(); CreateMap<TemplateType, TemplateTypeDTO>().ReverseMap(); CreateMap<Template, TemplateDTO>().ReverseMap(); CreateMap<User, UserDTO>(); CreateMap<AlertRecipient, AlertRecipientDto>().ReverseMap(); CreateMap<AlertUpdate, AlertUpdateDto>().ReverseMap(); // 基类Alert与AlertDTO的双向映射 CreateMap<Alert, AlertDTO>() .ForMember(dest => dest.UserName, opt => opt.MapFrom(src => src.Creator != null ? src.Creator.UserName : null)) .ReverseMap(); // 子类OperationalAlert与OperationalAlertDto的双向映射,包含基类字段 CreateMap<OperationalAlert, OperationalAlertDto>() .IncludeBase<Alert, AlertDTO>() .ReverseMap(); // 其他子类映射 CreateMap<AlertDTO, AlertInformational>().ReverseMap(); CreateMap<AlertDTO, AlertIncident>().ReverseMap(); }
3. 调试验证映射结果
在映射后添加调试代码,确认OperationalAlert实例的Content等字段是否被正确赋值:
var operationalAlert = _mapper.Map<OperationalAlert>(operationalDto); // 调试输出或断点检查operationalAlert.Content的值 Console.WriteLine($"Mapped Content: {operationalAlert.Content}");
4. 确认实体与数据库字段的匹配
检查OperationalAlert类的Content字段是否添加了[Required]特性,确保EF Core正确识别该字段为必填项;同时确认数据库中对应的字段设置为NOT NULL,避免配置不一致。
内容的提问来源于stack exchange,提问作者PASHA
相关产品推荐
相关产品推荐

