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

使用AutoMapper ProjectTo映射可空decimal到自定义类遇空值报错求助

问题:AutoMapper ProjectTo处理可空decimal到自定义类的Null映射异常

我正在使用AutoMapper通过ProjectTo<>()将Entity Framework的数据库实体映射到业务对象。想要将可空decimal类型自动映射到自定义类类型(比如Density),因此使用了自定义类型转换器,但当decimal?属性为null时,会抛出“Null TypeMapping in Sql Tree”异常,且转换器的Convert()方法完全不会被调用。相关代码如下:

基础类及派生类示例

public abstract class BaseUnit
{
    public decimal? DefaultValue { get; protected set; }
    public abstract ViewableUnit GetRequestedUnit(Unit unit);
}

public class Density : BaseUnit
{
    public Density()
    {
        this.DefaultValue = decimal.Zero;
    }
    
    public Density(decimal? densityInGperL)
    {   
        this.DefaultValue = densityInGperL;
    }

    public decimal? GramsPerLiter
    {
        get => this.DefaultValue;
        set => this.DefaultValue = value;
    }

    public decimal? GramsPerDeciliter
    {
        get => this.DefaultValue * 10;
        set => this.DefaultValue = value * 0.1m;
    }

    public override ViewableUnit GetRequestedUnit(Unit unit)
    {
        // 实现逻辑
    }
}

转换器及注册代码

public class DecimalUnitTypeConverter : ITypeConverter<decimal?, Density>
{
    public Density Convert(decimal? source, Density destination, ResolutionContext context)
    {
        return source.HasValue ? new Density(source.Value) : null;
    }
}

Mapper.CreateMap(typeof(decimal?), typeof(Density)).ConvertUsing(typeof(DecimalUnitTypeConverter));

数据库实体及DTO

public class LabResult
{
    public decimal? NumberResult { get; set; }
    // 其他属性
}

public class LabResultDto
{
    public Density NumberResult { get; set; }

    public void CreateMappings(Profile configuration)
    {
        configuration.CreateMap<LabResult, LabResultDto>()
        .ForMember(
         dst => dst.NumberResult,
         opt =>
         {
             opt.PreCondition(src => src.NumberResult.HasValue); // 有无此行结果一致
             opt.MapFrom(src => src.NumberResult);
         });
    }
}

最终使用代码

dbContext.LabResults.AsNoTracking()
.Where(lab => lab.Id == request.Id)
.ProjectTo<LabResultDto>(this.configurationProvider)
.ToList();

解决方案

核心原因

AutoMapper的ProjectTo<>()是基于LINQ to SQL的投影机制,会把映射逻辑转换成SQL表达式交给EF执行。而ITypeConverter是内存中执行的转换器,无法被EF的查询提供者识别,当源值为null时,EF无法生成对应的SQL来构造自定义类,因此抛出异常。

解决方法1:使用AutoMapper投影配置(推荐)

用ProjectUsing代替ITypeConverter,它能将映射逻辑转换成EF可识别的SQL表达式:

// 在Profile中配置decimal?到Density的投影
configuration.CreateMap<decimal?, Density>()
    .ProjectUsing(src => src.HasValue ? new Density(src.Value) : null);

同时简化LabResult到LabResultDto的映射,无需PreCondition:

public void CreateMappings(Profile configuration)
{
    configuration.CreateMap<LabResult, LabResultDto>()
        .ForMember(dst => dst.NumberResult, opt => opt.MapFrom(src => src.NumberResult));
}

解决方法2:先加载到内存再映射(适合复杂逻辑)

如果自定义类的构造/属性逻辑无法转换成SQL(比如复杂计算),可以先把数据加载到内存,再用AutoMapper的Map()方法触发类型转换器:

dbContext.LabResults.AsNoTracking()
    .Where(lab => lab.Id == request.Id)
    .ToList() // 先将数据加载到内存
    .Select(lab => mapper.Map<LabResultDto>(lab))
    .ToList();

注意:这种方式会把符合条件的所有数据加载到内存,数据量大时需权衡性能。


内容的提问来源于stack exchange,提问作者Peter Grodin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 12:27:23