You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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.
       ---&gt; 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 22:14:49