取消

使用 AutoMapper 自动在多个数据模型间进行转换

访问数据库、IPC 通信、业务模型、视图模型……对于同一个业务的同一种数据,经常会使用多种数据模型工作在不同的代码模块中。这时它们之间的互相转换便是大量的重复代码了。

使用 AutoMapper 便可以很方便地在不同的模型之间进行转换而减少编写太多的转换代码(如果这一处的代码对性能不太敏感的话)。


关于 AutoMapper 的系列文章:

安装 AutoMapper 库

这是 AutoMapper 的官方 GitHub 仓库:

安装 AutoMapper 的 NuGet 包即可在项目中使用 AutoMapper。

入门

以下是一个最简单的控制台演示程序的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Program.cs
var mapper = InitializeMapper();

var dao = new Walterlv1Dao
{
    Id = "2ed3558ac938438fb2c1d2de71d7bb90",
    Name = "walterlv",
    Text = "blog.walterlv.com",
};
var vo = mapper.Map<Walterlv1Vo>(dao);
Console.WriteLine($"Name = {vo.Name}, Text = {vo.Text}");

static IMapper InitializeMapper()
{
    var configuration = new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<Walterlv1Dao, Walterlv1Vo>();
    });
#if DEBUG
    configuration.AssertConfigurationIsValid();
#endif
    var mapper = configuration.CreateMapper();
    return mapper;
}

在这段代码中:

  1. 我们定义了一个方法 InitializeMapper,在里面初始化 IMapper 的新实例。
    • 初始化 MapperConfiguration,定义类型的映射关系
    • DEBUG 下验证 MapperConfiguration 的映射是否正确
    • 创建一个 IMapper 的映射器,用于后续映射使用
  2. 我们初始化了一个 Walterlv1Dao 类的实例
  3. 我们调用 mapper.Map 将其映射到 Walterlv1Vo 类型

这两个类型的定义如下(虽然无关紧要)。

1
2
3
4
5
6
7
8
9
10
11
12
public class Walterlv1Dao
{
    public string? Id { get; set; }
    public string? Name { get; set; }
    public string? Text { get; set; }
}
public class Walterlv1Vo
{
    public string? Id { get; set; }
    public string? Name { get; set; }
    public string? Text { get; set; }
}

如果你的应用程序中会使用到依赖注入,那么只需要把拿到的 IMapper 加入即可。

如果希望两个类型之间能够双向映射,那么在初始化 IMapper 的时候也应该再额外调用一下 ReverseMap 方法,否则就会抛出异常 AutoMapper.AutoMapperMappingException:“Missing type map configuration or unsupported mapping.”

1
cfg.CreateMap<Walterlv1Dao, Walterlv1Vo>().ReverseMap();

复杂类型和集合

现在,我们让模型稍复杂一些:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Walterlv1Dao
{
    public string? Id { get; set; }
    public string? Name { get; set; }
    public FriendDao? Friend { get; set; }
}
public class FriendDao
{
    public string? Id { get; set; }
    public string? Name { get; set; }
}
public class Walterlv1Vo
{
    public string? Id { get; set; }
    public string? Name { get; set; }
    public FriendVo? Friend { get; set; }
}
public class FriendVo
{
    public string? Id { get; set; }
    public string? Name { get; set; }
}

AutoMapper 能处理这样的属性嵌套情况,只需要设置嵌套类型也能映射即可:

1
2
cfg.CreateMap<Walterlv1Dao, Walterlv1Vo>().ReverseMap();
cfg.CreateMap<FriendDao, FriendVo>().ReverseMap();

如果两个模型中子模型的类型是一样的,那么只会进行简单的赋值,而不会创建新的对象。

例如上面例子里,如果 FriendDaoFriendVo 合并成 Friend 类型,两个类型都使用这个合并的类型,那么映射之后,Friend 将是同一个对象。

除了复杂类型,列表也是可以的:

1
2
3
4
5
6
7
8
9
10
11
12
public class Walterlv1Dao
{
    public string? Id { get; set; }
    public string? Name { get; set; }
    public List<FriendDao>? Friend { get; set; }
}
public class Walterlv1Vo
{
    public string? Id { get; set; }
    public string? Name { get; set; }
    public List<FriendVo>? Friend { get; set; }
}

参考资料

本文会经常更新,请阅读原文: https://blog.walterlv.com/post/convert-models-using-auto-mapper ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

知识共享许可协议

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 吕毅 (包含链接: https://blog.walterlv.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 ([email protected])

登录 GitHub 账号进行评论