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

C# AutoMapper父类型列表未映射子类问题求助

问题说明

尝试映射FirewallRule的子类,但控制器接收请求时,firewallDto中的DestinationPorts未被映射,数据被截断,仅保留FirewallRule层级结构。试过多种AutoMapper映射配置均无效,直接读取请求体时数据结构正常,推测是映射配置存在问题或遗漏关键设置。需求:请求体含DestinationPorts时映射为NetworkRule,含FqdnTags时映射为ApplicationRule。

请求体示例

{
  "id": 0,
  "name": "string",
  "priority": 0,
  "action": "string",
  "rules": [
        {
            "ruleType": "NetworkRule",
            "name": "myName",
            "description": "mydescription",
            "DestinationPorts": [""]
        }
  ]
}

控制器接收后的结构

{
  "id": 0,
  "name": "string",
  "priority": 0,
  "action": "string",
  "rules": [
        {
            "ruleType": "NetworkRule",
            "name": "myName",
            "description": "mydescription"
        }
  ]
}

代码定义

实体类与DTO类

// Classes
public class Firewall 
{
    public string Name { get; set; }
    public int Priority { get; set; }
    public string Action { get; set; }
    public List<FirewallRule> Rules { get; set; }
}

public class FirewallRule
{
    public string RuleType { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
}

public class ApplicationRule : FirewallRule
{
    public string[] FqdnTags { get; set; }
}

public class NetworkRule : FirewallRule
{
    public string[] DestinationPorts { get; set; }
}

public class FirewallDto
{
    public string Name { get; set; }
    public int Priority { get; set; }
    public string Action { get; set; }
    public List<FirewallRuleDto> Rules { get; set; }
}

public class FirewallRuleDto
{
    public string RuleType { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
}

public class NetworkRuleDto : FirewallRuleDto
{
    public string[] DestinationPorts { get; set; }
}

public class ApplicationRuleDto : FirewallRuleDto
{
    public string[] FqdnTags { get; set; }
}

AutoMapper映射配置

// Mapping
CreateMap<Firewall, FirewallDto>().
    ReverseMap();

CreateMap<FirewallRule, FirewallRuleDto>()
    .Include<NetworkRule, NetworkRuleDto>()
    .Include<ApplicationRule, ApplicationRuleDto>().
    ForMember(dest => dest.RuleType, opt => opt.MapFrom(src => src.RuleType));

CreateMap<NetworkRule, NetworkRuleDto>()
    .ReverseMap();

CreateMap<ApplicationRule, ApplicationRuleDto>()
    .ReverseMap();

CreateMap<FirewallRuleDto, FirewallRule>()
    .Include<NetworkRuleDto, NetworkRule>()
    .Include<ApplicationRuleDto, ApplicationRule>();

CreateMap<FirewallDto, Firewall>().
    ForMember(d => d.Rules, op => op.MapFrom(s => s.Rules));

控制器代码

// Controller
public async Task<ActionResult<Firewall>> NewFirewall(FirewallDto firewallDto) 
{
    // code
}

解决方案

问题核心有两点:一是JSON序列化/反序列化时无法自动识别子类,控制器接收FirewallRuleDto列表时,所有子类实例都会被反序列化为基类FirewallRuleDto,导致子类属性丢失;二是AutoMapper的映射配置缺少基于RuleType的类型转换逻辑。

步骤1:配置JSON序列化支持多态

在ASP.NET Core的Program.cs中配置System.Text.Json支持多态,指定基于RuleType字段识别子类:

builder.Services.AddControllers()
    .AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.TypeInfoResolverChain.Insert(0, FirewallRuleDtoPolymorphicResolver.Default);
    });

// 自定义多态解析器
public class FirewallRuleDtoPolymorphicResolver : DefaultJsonTypeInfoResolver
{
    public static FirewallRuleDtoPolymorphicResolver Default { get; } = new FirewallRuleDtoPolymorphicResolver();

    public FirewallRuleDtoPolymorphicResolver()
    {
        Modifiers.Add(typeInfo =>
        {
            if (typeInfo.Type == typeof(FirewallRuleDto))
            {
                typeInfo.PolymorphismOptions = new JsonPolymorphismOptions
                {
                    TypeDiscriminatorPropertyName = "RuleType",
                    IgnoreUnrecognizedTypeDiscriminators = true,
                    DerivedTypes =
                    {
                        new JsonDerivedType(typeof(NetworkRuleDto), "NetworkRule"),
                        new JsonDerivedType(typeof(ApplicationRuleDto), "ApplicationRule")
                    }
                };
            }
        });
    }
}

这段配置让JSON序列化器在遇到FirewallRuleDto类型时,根据RuleType字段的值自动反序列化为对应的子类(NetworkRuleDto或ApplicationRuleDto),确保子类属性不丢失。

步骤2:修正AutoMapper映射配置

移除重复的FirewallDto到Firewall映射,确保子类映射正确生效,同时明确正向和反向映射的Include关系:

// 正向映射:实体到DTO
CreateMap<Firewall, FirewallDto>().ReverseMap();

CreateMap<FirewallRule, FirewallRuleDto>()
    .Include<NetworkRule, NetworkRuleDto>()
    .Include<ApplicationRule, ApplicationRuleDto>();

CreateMap<NetworkRule, NetworkRuleDto>().ReverseMap();
CreateMap<ApplicationRule, ApplicationRuleDto>().ReverseMap();

// 反向映射:DTO到实体
CreateMap<FirewallRuleDto, FirewallRule>()
    .Include<NetworkRuleDto, NetworkRule>()
    .Include<ApplicationRuleDto, ApplicationRule>()
    .ConstructUsing((dto, context) => 
    {
        // 基于RuleType创建对应的实体子类实例
        return dto.RuleType switch
        {
            "NetworkRule" => new NetworkRule(),
            "ApplicationRule" => new ApplicationRule(),
            _ => new FirewallRule()
        };
    });

添加ConstructUsing方法,让AutoMapper在反向映射时,根据DTO的RuleType字段创建对应的实体子类实例,确保子类属性能正确映射。

步骤3:验证控制器接收逻辑

现在控制器接收FirewallDto时,Rules列表中的项会被正确反序列化为NetworkRuleDto或ApplicationRuleDto,再通过AutoMapper映射到对应的实体子类,属性不会丢失。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 02:46:22