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

ASP.NET Core AutoMapper映射错误:如何定位问题属性?

排查ASP.NET Core中AutoMapper映射ServicePlan到ServicePlanModel的缺失配置错误

问题描述

在ASP.NET Core中尝试将ServicePlan实体映射到ServicePlanModel模型时,触发AutoMapperMappingException错误,提示**"缺少类型映射配置或不支持的映射"**。已配置AutoMapperProfile类,调试后仍无法定位具体出错属性,寻求排查方法。

错误栈信息

Error: AutoMapper.AutoMapperMappingException: 'Error mapping types.'
AutoMapperMappingException: Missing type map configuration or unsupported mapping.

This exception was originally thrown at this call stack:
AutoMapper.Mapper.MapCore<TSource, TDestination>(TSource, TDestination, AutoMapper.ResolutionContext, System.Type, System.Type, AutoMapper.MemberMap)

AutoMapper.Mapper.AutoMapper.IInternalRuntimeMapper.Map<TSource, TDestination>(TSource, TDestination, AutoMapper.ResolutionContext, System.Type, System.Type, AutoMapper.MemberMap)

AutoMapper.ResolutionContext.MapInternal<TSource, TDestination>(TSource, TDestination, AutoMapper.MemberMap)

相关代码

AutoMapperProfile配置

public class AutoMapperProfile : Profile
{
    public AutoMapperProfile()
    {
        CreateMap<Account, AccountModel>();
        CreateMap<AccountModel, Account>();

        CreateMap<ServicePlan, ServicePlanModel>()
              .ForMember(ServicePlanModel => ServicePlanModel.ServicePlanDetails, opt => opt.MapFrom(src => src.ServicePlanDetails.ToList()))
              .ForMember(dest => dest.Lines, opt => opt.MapFrom(src => src.NLines ?? default))
              .ForMember(dest => dest.StartDate, opt => opt.MapFrom(src => src.DateStart))
              .ForMember(dest => dest.EndDate, opt => opt.MapFrom(src => src.DateEnd))
              .ForMember(dest => dest.MonthlyCharges, opt => opt.MapFrom(src => src.AmountMonthly))
              .ForMember(dest => dest.OneTimeCharges, opt => opt.MapFrom(src => src.AmountOnetime));
    }
}

映射调用代码

public async Task<List<ServicePlanModel>> GetServicePlansWithDetails(long accountId)
{
    var plan = mapper.Map<ServicePlan, ServicePlanModel>(servicePlan);
}

ServicePlan实体类

public partial class ServicePlan
{
    public ServicePlan()
    {
        ServicePlanDetails = new HashSet<ServicePlanDetail>();
    }

    public long PlanId { get; set; }
    public decimal PhoneId { get; set; }
    public byte? NLines { get; set; }
    public DateTime? DateStart { get; set; }
    public DateTime? DateEnd { get; set; }
    public decimal? AmountMonthly { get; set; }
    public decimal? AmountOnetime { get; set; }
    public string? ContractType { get; set; }
    public string? CallerId { get; set; }
    public bool? Active { get; set; }
    public short? ResellerId { get; set; }
    public int? OnetimeInvoiceNo { get; set; }
    public bool? IsRequest { get; set; }
    public string? RequestType { get; set; }
    public string? NewValue { get; set; }
    public DateTime? RequestDate { get; set; }
    public string? RequestBy { get; set; }
    public bool? IsRejected { get; set; }
    public bool? IsApproved { get; set; }
    public DateTime? ApprovedWhen { get; set; }
    public string? ApprovedBy { get; set; }
    public string? Comments { get; set; }
    public string? AdditionalDetails { get; set; }
    public string? VideoTronId { get; set; }
    public string? Zone { get; set; }
    public string? PrevProvider { get; set; }
    public bool? Charge911Tax { get; set; }
    public string? ContractTerms { get; set; }
    public long? ContractId { get; set; }
    public int AllowedBandwidth { get; set; }
    public DateTime? ServiceStartDate { get; set; }
    public string? AppointmentTime { get; set; }
    public DateTime? AppointmentEmailSentOn { get; set; }
    public string? TrackingNumber { get; set; }
    public bool? HasNumberPort { get; set; }
    public DateTime? EstPortDate { get; set; }
    public DateTime? PortCompletedOn { get; set; }
    public bool? IsExGbchargesLimited { get; set; }
    public decimal? ExGbchagesLimitAmt { get; set; }
    public bool? IsPreVisitSch { get; set; }
    public DateTime? PreVisitSchDate { get; set; }
    public string? PreVisitSchTime { get; set; }
    public bool? MoniterBwUsage { get; set; }
    public int? BwWarnLimit { get; set; }
    public string? DslaccountId { get; set; }
    public string? BellGisId { get; set; }
    public string? DslPassword { get; set; }
    public bool? IsPasswordPassed { get; set; }
    public bool? IsDslUserPassed { get; set; }
    public string? CogecoId { get; set; }
    public int? ServiceProviderId { get; set; }
    public string? RogersId { get; set; }
    public decimal? InternetSpeed { get; set; }
    public bool? ModemRental { get; set; }
    public bool? RouterRental { get; set; }
    public bool? Atarental { get; set; }
    public string? DslaccountDomain { get; set; }

    [NotMapped]
    public virtual ICollection<ServicePlanDetail> ServicePlanDetails { get; set; }
}

ServicePlanModel模型类

public class ServicePlanModel
{
    public ServicePlanModel()
    {
        ServicePlanDiscounts = new List<ServicePlanDiscount>();
        ServicePlanDetails = new List<ServicePlanDetail>();
    }

    [Key]
    public Int64 PlanId { get; set; }
    public Int64? ContractId { get; set; }
    public int ResellerId { get; set; }
    public Int64 PhoneId { get; set; }
    public bool IsPhone { get; set; }
    public Int32? OneTimeInvoiceId { get; set; }
    public UInt16 Lines { get; set; }
    public DateTime StartDate { get; set; }
    public DateTime? EndDate { get; set; }
    public Decimal MonthlyCharges { get; set; }
    public Decimal OneTimeCharges { get; set; }
    public String ContractType { get; set; }
    public String CallerId { get; set; }
    public Boolean IsActive { get; set; }
    public Boolean IsRequest { get; set; }
    public Boolean IsRejected { get; set; }
    public Boolean IsApproved { get; set; }
    public Boolean Charge911Tax { get; set; }
    public DateTime ApprovedOn { get; set; }
    public String ApprovedBy { get; set; }
    public String Comments { get; set; }
    public String ContractTerms { get; set; }
    public String AdditionalDetails { get; set; }
    public String VideoTronId { get; set; }
    public String Zone { get; set; }
    public String PreviousProvider { get; set; }
    public String RequestType { get; set; }
    public String NewValue { get; set; }
    public DateTime? RequestDate { get; set; }
    public String RequestedBy { get; set; }
    //public PhoneNumber PhoneNumber { get; set; }
    public List<ServicePlanDetail> ServicePlanDetails { get; set; }
    public List<ServicePlanDiscount> ServicePlanDiscounts { get; set; }

    public bool IsLandLinePlan
    {
        get
        {
            if (ServicePlanDetails != null)
            {
                return ServicePlanDetails.Any(spd => new String[] { "landline-r", "landline-b", "plan_10103", "plan_10104", "plan_10105" }.Contains(spd.ServiceCode));
            }

            return false;
        }
    }

    public String ServicePlanName
    {
        get
        {
            if (ServicePlanDetails != null && ServicePlanDetails.Count > 0)
            {
                var plan = ServicePlanDetails.SingleOrDefault(spd => spd.IsPlan);
                if (plan != null)
                {
                    if (IsPhone)
                    {
                        return String.Format("{0} ({3}) ({1} x {2})", plan.ServiceTitle, plan.UnitPrice.ToString("N2"), plan.Quantity, PhoneId.ToString("###-###-####"));
                    }

                    return String.Format("{0} ({1} x {2})", plan.ServiceTitle, plan.UnitPrice.ToString("N2"), plan.Quantity);
                }
            }
            return String.Empty;
        }
    }

    public String MonthlyChargesDetails
    {
        get
        {
            if (ServicePlanDetails != null && ServicePlanDetails.Count > 0)
            {
                var items = ServicePlanDetails.Where(spd => spd.IsPlan == false & spd.UnitPrice > 0 & spd.ServiceType.Equals(Constants.SERVICE_TYPE_MONTHLY)).ToList();

                var details = new StringBuilder();

                foreach (var spd in items)
                {
                    details.Append(String.Format("{0} ({1} x {2}) ", spd.ServiceTitle, spd.UnitPrice.ToString("N2"),
                                                 spd.Quantity));
                }

                return details.ToString();
            }

            return String.Empty;
        }
    }

    public List<ServicePlanDetail> OnetimeChargesDetails
    {
        get
        {
            if (ServicePlanDetails != null && ServicePlanDetails.Count > 0)
            {
                return ServicePlanDetails.Where(spd => spd.IsPlan == false & spd.ServiceType.Equals(Constants.SERVICE_TYPE_ONETIME)).ToList();
            }

            return null;
        }
    }

    public List<ServicePlanDetail> FreeServicesDetails
    {
        get { return ServicePlanDetails != null ? ServicePlanDetails.Where(s => s.UnitPrice == 0 && !s.ServiceType.Equals("month")).ToList() : null; }
    }

    public List<ServicePlanDetail> DiscountedServicesDetail
    {
        get
        {
            return ServicePlanDetails != null ?
            ServicePlanDetails.Where(s => s.ResellerService != null && s.UnitPrice < s.ResellerService.SuggestedPrice && !s.ServiceType.Equals("month")).ToList() : null;
        }
    }

    public string Modem_MAC { get; set; }
    public string TrackingNumber { get; set; }
}

排查步骤

  • 验证AutoMapper配置注册
    确认在Program.cs(或Startup.cs)中已正确注册AutoMapper并加载配置文件:

    builder.Services.AddAutoMapper(typeof(AutoMapperProfile));
    

    若使用程序集扫描,确保包含AutoMapperProfile所在的程序集。

  • 检查值类型可空性与强制类型转换
    对比实体与模型的属性类型,处理可空转非可空的场景:

    • DateStart(DateTime?)转StartDate(非可空DateTime)需添加空值兜底:
      .ForMember(dest => dest.StartDate, opt => opt.MapFrom(src => src.DateStart ?? DateTime.MinValue))
      
    • AmountMonthly/AmountOnetime(decimal?)转非可空Decimal需处理空值:
      .ForMember(dest => dest.MonthlyCharges, opt => opt.MapFrom(src => src.AmountMonthly ?? 0))
      .ForMember(dest => dest.OneTimeCharges, opt => opt.MapFrom(src => src.AmountOnetime ?? 0))
      
  • 补充未显式映射的属性
    对比两个类的属性,补充名称或类型不匹配的映射规则:

    .ForMember(dest => dest.IsActive, opt => opt.MapFrom(src => src.Active ?? false))
    .ForMember(dest => dest.ResellerId, opt => opt.MapFrom(src => src.ResellerId ?? 0))
    .ForMember(dest => dest.OneTimeInvoiceId, opt => opt.MapFrom(src => src.OnetimeInvoiceNo))
    .ForMember(dest => dest.ApprovedOn, opt => opt.MapFrom(src => src.ApprovedWhen ?? DateTime.MinValue))
    .ForMember(dest => dest.PreviousProvider, opt => opt.MapFrom(src => src.PrevProvider))
    .ForMember(dest => dest.RequestedBy, opt => opt.MapFrom(src => src.RequestBy))
    
  • 启用AutoMapper配置验证
    在注册时添加配置验证,启动时直接抛出未配置的属性错误:

    builder.Services.AddAutoMapper(cfg =>
    {
        cfg.AddProfile<AutoMapperProfile>();
        cfg.ConfigurationProvider.AssertConfigurationIsValid();
    }, typeof(AutoMapperProfile));
    
  • 查看内部异常详情
    捕获AutoMapperMappingException后,检查InnerException属性,获取具体出错的属性信息。

  • 检查嵌套对象映射
    确认ServicePlanDetail等嵌套类型是否存在未配置的映射需求,若有需补充对应的CreateMap配置。


内容的提问来源于stack exchange,提问作者Shahreyar Butt

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 21:25:13