项目
版本

AutoMapper 展平(Flattening)

对象到对象映射的一个常见用途是将复杂的对象模型展平为更简单的模型。例如,您可以将如下复杂的模型:

public class Order
{
	private readonly IList<OrderLineItem> _orderLineItems = new List<OrderLineItem>();

	public Customer Customer { get; set; }

	public OrderLineItem[] GetOrderLineItems()
	{
		return _orderLineItems.ToArray();
	}

	public void AddOrderLineItem(Product product, int quantity)
	{
		_orderLineItems.Add(new OrderLineItem(product, quantity));
	}

	public decimal GetTotal()
	{
		return _orderLineItems.Sum(li => li.GetTotal());
	}
}

public class Product
{
	public decimal Price { get; set; }
	public string Name { get; set; }
}

public class OrderLineItem
{
	public OrderLineItem(Product product, int quantity)
	{
		Product = product;
		Quantity = quantity;
	}

	public Product Product { get; private set; }
	public int Quantity { get; private set;}

	public decimal GetTotal()
	{
		return Quantity*Product.Price;
	}
}

public class Customer
{
	public string Name { get; set; }
}

我们希望将这个复杂的 Order 对象展平为一个更简单的只包含特定场景所需数据的 OrderDto

public class OrderDto
{
    public string CustomerName { get; set; }
    public decimal Total { get; set; }
}

AutoMapper 中配置源类型和目标类型对时,配置器会尝试将源类型上的属性和方法与目标类型上的属性进行匹配。如果目标类型上任何属性在源类型上没有对应的属性、方法或以 "Get" 开头的方法,则 AutoMapper 会根据 PascalCase 约定将目标成员名拆分成单个单词。

// 复杂模型示例

var customer = new Customer
{
    Name = "George Costanza"
};
var order = new Order
{
    Customer = customer
};
var bosco = new Product
{
    Name = "Bosco",
    Price = 4.99m
};
order.AddOrderLineItem(bosco, 15);

// 配置AutoMapper

var configuration = new MapperConfiguration(cfg => cfg.CreateMap<Order, OrderDto>());

// 执行映射

OrderDto dto = mapper.Map<Order, OrderDto>(order);

dto.CustomerName.ShouldBe("George Costanza");
dto.Total.ShouldBe(74.85m);

我们使用 CreateMap 方法在 AutoMapper 中配置了类型映射。AutoMapper 只能映射已知的类型对,因此我们必须显式注册源/目标类型对。进行映射时,我们使用 Map 方法。

OrderDto 类型中,Total 属性与 Order 上的 GetTotal() 方法相匹配。CustomerName 属性与 Order 上的 Customer.Name 属性相匹配。只要我们适当地命名目标属性,就不需要单独配置属性匹配。

如果你想禁用此行为,可以使用 ExactMatchNamingConvention

cfg.DestinationMemberNamingConvention = new ExactMatchNamingConvention();

IncludeMembers

如果你在展平时需要更多控制,可以使用 IncludeMembers 。当已有从子类型到目标类型的映射时(不同于经典展平,后者不需要为子类型提供映射),你可以映射子对象的成员到目标对象。

class Source
{
    public string Name { get; set; }
    public InnerSource InnerSource { get; set; }
    public OtherInnerSource OtherInnerSource { get; set; }
}
class InnerSource
{
    public string Name { get; set; }
    public string Description { get; set; }
}
class OtherInnerSource
{
    public string Name { get; set; }
    public string Description { get; set; }
    public string Title { get; set; }
}
class Destination
{
    public string Name { get; set; }
    public string Description { get; set; }
    public string Title { get; set; }
}

cfg.CreateMap<Source, Destination>().IncludeMembers(s=>s.InnerSource, s=>s.OtherInnerSource);
cfg.CreateMap<InnerSource, Destination>(MemberList.None);
cfg.CreateMap<OtherInnerSource, Destination>();

var source = new Source { Name = "name", InnerSource = new InnerSource{ Description = "description" }, 
                          OtherInnerSource = new OtherInnerSource{ Title = "title" } };
var destination = mapper.Map<Destination>(source);
destination.Name.ShouldBe("name");
destination.Description.ShouldBe("description");
destination.Title.ShouldBe("title");

这允许你在映射父类型 SourceDestination 时重用子类型 InnerSourceOtherInnerSource 现有映射中的配置。其工作方式类似于 映射继承 ,但使用的是组合而非继承。

IncludeMembers 调用中参数的顺序很重要。当映射目标成员时,首先匹配的获胜,从源对象本身开始,然后按照你指定的顺序匹配包含的子对象。所以在上面的例子中,Name 是从源对象本身映射的,而 Description 来自 InnerSource ,因为它是第一个匹配项。

请注意,这种匹配是静态的,在配置时发生,而不是在 Map 时,所以子对象的运行时类型不被考虑。

IncludeMembersReverseMap 集成。包含的成员将被反转为

ForPath(destination => destination.IncludedMember, member => member.MapFrom(source => source))

反之亦然。如果你不希望这样,可以避免使用 ReverseMap(显式创建反向映射)或覆盖默认设置(使用 Ignore 或无参数的 IncludeMembers )。

详情请查看 测试代码

在本文档中