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

Automapper:如何仅在子属性有映射值时才创建父对象?

问题:如何让AutoMapper仅在父对象有属性映射时才创建父对象

需要将ShoppingCart映射为Order,规则如下:

  • 当购物车中存在名为"Pizza"的Item时,Order.Pizza需被实例化,且Price取自该Item的价格
  • 若购物车中无该Item,Order.Pizza需为null,而非默认实例化的空对象

模型定义

public class ShoppingCart
{
   public List<Item> Items { get; set; }
}

public class Item 
{
   public string Name { get; set; }
   public int Price { get; set; }
}

public class Order
{
   public string Id { get; set; }
   public Pizza Pizza { get; set; }
}

public class Pizza
{
   public int Price { get; set; }
}

测试用例

[Test]
public void Test_WithPizza()
{
    // Arrange
    var cart = new ShoppingCart()
    {
        Items = new List<Item>()
        {
            new Item() { Name = "Cola", Price = 10 },
            new Item() { Name = "Pizza", Price = 20 }
        }
    };

    //Act
    var order = Mapper.Map<ShoppingCart, Order>(cart);

    //Assert
    Assert.IsNotNull(order);
    Assert.IsNotNull(order.Pizza);
    Assert.AreEqual(20, order.Pizza.Price);
}

[Test]
public void Test_WithoutPizza()
{
    // Arrange
    var cart = new ShoppingCart()
    {
        Items = new List<Item>()
        {
            new Item() { Name = "Cola", Price = 10 }
        }
    };

    //Act
    var order = Mapper.Map<ShoppingCart, Order>(cart);

    //Assert
    Assert.IsNotNull(order);
    Assert.IsNull(order.Pizza); // 原配置此处失败
}

原配置问题

原配置通过ForPath给Pizza.Price加条件,但AutoMapper会先实例化Pizza父对象再处理属性映射,因此无法阻止空对象的创建:

public class MyProfile : Profile
{
  public MyProfile()
  {
    CreateMap<ShoppingCart, Order>()
      .ForMember(dest => dest.Id, opt => opt.MapFrom(_ => Guid.NewGuid()))
      .ForPath(dest => dest.Pizza.Price, opt =>
      {
        opt.MapFrom(src => GetPizzaPrice(src));
        opt.Condition(con => GetPizzaPrice(con.Source) != null);
      });
  }

  private static int? GetPizzaPrice(ShoppingCart cart)
    => cart.Items.SingleOrDefault(i => i.Name == "Pizza")?.Price;
}

解决方案

方法1:直接映射Pizza对象(推荐)

直接对Order.Pizza字段配置映射逻辑,通过ResolveUsing控制是否创建实例:

public class MyProfile : Profile
{
    public MyProfile()
    {
        CreateMap<ShoppingCart, Order>()
            .ForMember(dest => dest.Id, opt => opt.MapFrom(_ => Guid.NewGuid().ToString()))
            .ForMember(dest => dest.Pizza, opt => opt.ResolveUsing(src => 
            {
                var pizzaItem = src.Items.SingleOrDefault(i => i.Name == "Pizza");
                return pizzaItem != null ? new Pizza { Price = pizzaItem.Price } : null;
            }));
    }
}

此方法直接掌控Pizza对象的创建时机,逻辑清晰,完全符合需求。

方法2:条件映射父对象

先为Order.Pizza设置创建条件,再映射子属性:

public class MyProfile : Profile
{
    public MyProfile()
    {
        CreateMap<ShoppingCart, Order>()
            .ForMember(dest => dest.Id, opt => opt.MapFrom(_ => Guid.NewGuid().ToString()))
            .ForMember(dest => dest.Pizza, opt => 
            {
                // 仅当存在Pizza Item时才创建Pizza实例
                opt.Condition(src => src.Items.Any(i => i.Name == "Pizza"));
                opt.MapFrom(src => new Pizza());
            })
            .ForPath(dest => dest.Pizza.Price, opt => 
            {
                opt.MapFrom(src => src.Items.Single(i => i.Name == "Pizza").Price);
            });
    }
}

当条件不满足时,Order.Pizza会保持null,不会被实例化。


两种方案均可通过上述测试用例,实现需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 21:05:22