> For the complete documentation index, see [llms.txt](https://monksoul.gitbook.io/hoa/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://monksoul.gitbook.io/hoa/duixiangyingshe.md).

# 八、对象映射指南

## 传统模式（不推荐）

假设有两个类型：`Student` 和 `StudentDto`，现在需要将 `Student` 对象赋值给 `StudentDto`

`Student`

```csharp
public class Student
{
    public string FirstName {get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
    public string Address { get; set; }
    public string Gender {get; set; }
}
```

`StudentDto`

```csharp
public class StudentDto
{
    public string FullName {get; set; }
    public int Age { get; set; }
    public string Gender {get; set; }
}
```

传统赋值模式代码如下：

```csharp
var student = new Student() 
{
    FirstName = "Monk",
    LastName = "Soul",
    Age = 27,
    Address = "广东省珠海市香洲区吉大路",
    Gender = "男"
};

// 赋值

var studentDto = new StudentDto()
{
    FullName = student.FirstName + student.LastName,
    Age = student.Age,
    Gender = student.Gender
}
```

通过上面的代码，可能一部分读者觉的这是一件很正常的代码，没有什么不妥。

确实，这样的代码咋看没什么不妥，但是**有几个漏洞**：

1. 如果字段繁多，这赋值操作相当耗时间且容易出错
2. 如果多次需要将 Student映射到 StudentDto中，则这样的代码散落在很大地方，给维护带来了极大的困扰
3. 这样的赋值操作实际上污染了业务逻辑代码，不利于后续的统一管理
4. 这样的赋值操作实在是多，无意增加了项目包的大小，开发效率也会低下

所以，我们迫切需要一种更加灵活、简易的对象赋值（映射）方式。

## 对象映射组件（推荐）

正是由于传统组件带来的漏洞及效率低下问题，所以就有第三方开发者提供了解决方案，在众多的解决方案中，无外乎 [AutoMapper](https://github.com/AutoMapper/AutoMapper) 和 [Mapster](https://github.com/MapsterMapper/Mapster) 最为出色。

由于[ Mapster](https://github.com/MapsterMapper/Mapster) 简单易用，而且性能极高，所以 Hoa Framework 将 Mapster 作为默认的映射组件。

## Mapster 使用

### 简单使用

将源对象映射到目标对象，并**创建新的目标对象**

```csharp
var destObject = sourceObject.Adapt<Destination>();
```

将源对隐射到**已存在的目标对象**

```csharp
var destObject = new DestObject(){...};
sourceObject.Adapt(destObject );
```

### 自定义映射规则

通常，我们的属性并非总是一一对应映射，我们可能会进行一些操作或重写，比如：`StudentDto.FullName` 由 `Student.FirstName` + `Student.LastName` 组成。

自定义映射支持多种方式，在 Hoa Framework 中，建议程序员将 自定义映射规则配置在 `Hoa.Application.ManualMapper.cs`中，方便集中管理。

```csharp
using Mapster;

namespace Hoa.Application
{
    public class ManualMapper : IRegister
    {
        public void Register(TypeAdapterConfig config)
        {
            config.ForType<SourceObject, DestinationObject>()
                .Map(dest => dest.FullName,
                     src => string.Format("{0} {1}", src.FirstName, src.LastName));

            config.ForType<DestinationObject, SourceObject>()
                .Map(dest => dest.Gender,
                     src => src.GenderString);
                     
            config.ForType<SourceObject, DestinationObject>()
                .Map(dest => dest.FullName, src => "Sig. " + src.FullName, srcCond => srcCond.Country == "Italy")
                .Map(dest => dest.FullName, src => "Sr. " + src.FullName, srcCond => srcCond.Country == "Spain")
                .Map(dest => dest.FullName, src => "Mr. " + src.FullName);
        }
    }
}
```

更多自定义映射规则[可查看官方文档](https://github.com/MapsterMapper/Mapster/wiki/Custom-mapping)。

### 特殊映射

Hoa Framework 内置了将类似 `GROUP_ID`->`GroupId`和`GroupId`->`GROUP_ID`两种映射方式，这是 [Mapster](https://github.com/MapsterMapper/Mapster) 默认不具备的功能。

配置`Hoa.Application.ManualMapper.cs`如下：

```csharp
using Mapster;
using System.Text;

namespace Hoa.Application.Authorization.Dtos
{
    public class Mapper : IRegister
    {
        public void Register(TypeAdapterConfig config)
        {
            config.ForType<SignInInput, SignInInput2>()
                .ConvertSourceMemberUnderlineSplitNameToCamelCase();    // 支持：GROUP_ID -> GroupId 方式

            config.ForType<SignInInput2, SignInInput>()
                .ConvertSourceMemberCamelCaseNameToUnderlineSplit();    // 支持：GroupId -> GROUP_ID 方式
        }
    }
}
```

### 数据库实体映射说明

将 EF Core 查询的结果集映射到 Dto 中。

#### :flag\_black: **传统模式（不推荐）：**

```csharp
using (MyDbContext context = new MyDbContext())
{
    // 不使用 Mapster 时候
    var destinations = context.Sources.Select(c => new Destination {
        Id = p.Id,
        Name = p.Name,
        Surname = p.Surname,
        ....
    })
    .ToList();
}
```

#### :flag\_black: **使用** [Mapster](https://github.com/MapsterMapper/Mapster) **之后（推荐）：**

```csharp
using (MyDbContext context = new MyDbContext())
{
    // 使用 Mapster 后
    var destinations = context.Sources.ProjectToType<Destination>().ToList();
}
```

### 将 `Dto` 映射到已存在的 `Entity`

```csharp
var entity = _testRepository.GetFirstOrDefault(predicate: u =>u.Id == 1);
var dto = new Dto() { Name = "Monk", Age = 27};
dto.Adapt(entity);  // 这时，Entity会自动更新 Name和Age属性，其他的保持不变
```

**注意** 如果 `Dto` 中某些值为 `Null`，默认也会将 `Null`值更新到 `Entity`，**这可能不是我们想要的！！！**&#x6240;以，我们需要手动配置一下，`Null` 值不需要映射。

```csharp
config.ForType<Dto, Entity>()
    .IgnoreNullValues(true);
```

更多  [Mapster](https://github.com/MapsterMapper/Mapster)  [可查看官方文档](https://github.com/MapsterMapper/Mapster/wiki)。
