前言

在项目中我们会经常遇到对象的映射,比如像Model和Dto之间的映射,或者是对象的深拷贝,这些都是需要我们自己实现的。此时,项目中会出现很多初始化对象的代码,这些代码写起来相当的枯燥乏味,那么有没有什么办法减轻我们的工作量,使得我们可以把时间花费到业务功能上呢?

目前,.Net中的对象映射框架,功能强大且性能极佳的对象映射框架已经存在,其中使用最多的有:

说到对象映射框架,大家想到的最多的是AutoMapper,可能很多人连Mapster都没听过,但不可否认的是Mapster确实是一个很好的对象映射框架,但由于中文文档的缺失,导致在国内知名度不是很高,今天我们就来介绍一下Mapster提供了哪些功能,如何在项目中使用它,Masa提供的Mapster又做了什么?

Mapster 简介

Mapster是一个使用简单,功能强大的对象映射框架,自2014年开源到现在已经过去8个年头,截止到现在,github上已经拥有2.6k的star,并保持着每年3次的发版频率,其功能与AutoMapper类似,提供对象到对象的映射、并支持IQueryable到对象的映射,与AutoMapper相比,在速度和内存占用方面表现的更加优秀,可以在只使用1/3内存的情况下获得4倍的性能提升,那我们下面就来看看Mapster如何使用?

准备工作

  • 新建一个控制台项目Assignment.Mapster,并安装Mapster

    dotnet add package Mapster --version 7.3.0

映射到新对象

  1. 新建类UserDto

    public class UserDto
    {
    public int Id { get; set; } public string Name { get; set; } public uint Gender { get; set; } public DateTime BirthDay { get; set; }
    }
  2. 新建一个匿名对象,作为待转换的对象源

    var user = new
    {
    Id = 1,
    Name = "Tom",
    Gender = 1,
    BirthDay = DateTime.Parse("2002-01-01")
    };
  3. 将user源对象映射到为目标对象 (UserDto)

    var userDto = user.Adapt<UserDto>();
    Console.WriteLine($"映射到新对象,Name: {userDto.Name}");

运行控制台程序验证转换成功:

数据类型

除了提供对象到对象的映射,还支持数据类型的转换,如:

基本类型

  • 提供类型映射的功能,类似Convert.ChangeType()

    string res = "123";
    decimal i = res.Adapt<decimal>(); //equal to (decimal)123;
    Console.WriteLine($"结果为:{i == int.Parse(res)}");

运行控制台程序:

枚举类型

  • 把枚举映射到数字类型,同样也支持字符串到枚举和枚举到字符串的映射,比.NET的默认实现快两倍

    var fileMode = "Create, Open".Adapt<FileMode>();//等于 FileMode.Create | FileMode.Open
    Console.WriteLine($"枚举类型转换的结果为:{fileMode == (FileMode.Create | FileMode.Open)}");

运行控制台程序验证转换成功:

Queryable扩展

Mapster提供了Queryable的扩展,用于实现DbContext的按需查找,例如:

  1. 新建类UserDbContext

    using Assignment.Mapster.Domain;
    using Microsoft.EntityFrameworkCore; namespace Assignment.Mapster.Infrastructure; public class UserDbContext : DbContext
    {
    public DbSet<User> User { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
    var dataBaseName = Guid.NewGuid().ToString();
    optionsBuilder.UseInMemoryDatabase(dataBaseName);//使用内存数据库,方便测试
    }
    }
  2. 新建类User

    public class User
    {
    public int Id { get; set; } public string Name { get; set; } public uint Gender { get; set; } public DateTime BirthDay { get; set; } public DateTime CreationTime { get; set; } public User()
    {
    CreationTime = DateTime.Now;
    }
    }
  3. 使用基于Queryable的扩展方法ProjectToType

    using (var dbContext = new UserDbContext())
    {
    dbContext.Database.EnsureCreated(); dbContext.User.Add(new User()
    {
    Id = 1,
    Name = "Tom",
    Gender = 1,
    BirthDay = DateTime.Parse("2002-01-01")
    });
    dbContext.SaveChanges(); var userItemList = dbContext.User.ProjectToType<UserDto>().ToList();
    }

运行控制台程序验证转换成功:

除此之外,Mapster还提供了映射前/后处理,拷贝与合并以及映射配置嵌套支持,详细可查看文档,既然Mapster已经如此强大,那我直接使用它就可以了,为什么还要使用Masa提供的Mapper呢?

什么是Masa.Contrib.Data.Mapping.Mapster?

Masa.Contrib.Data.Mapping.Mapster是基于Mapster的一个对象到对象的映射器,并在原来Mapster的基础上增加自动获取并使用最佳构造函数映射,支持嵌套映射,减轻映射的工作量。

映射规则

  • 目标对象没有构造函数时:使用空构造函数,映射到字段和属性。

  • 目标对象存在多个构造函数:获取最佳构造函数映射

    最佳构造函数: 目标对象构造函数参数数量从大到小降序查找,参数名称一致(不区分大小写)且参数类型与源对象属性一致

准备工作

  • 新建一个控制台项目Assignment.Masa.Mapster,并安装Masa.Contrib.Data.Mapping.MapsterMicrosoft.Extensions.DependencyInjection

    dotnet add package Masa.Contrib.Data.Mapping.Mapster --version 0.4.0-rc.4
    dotnet add package Microsoft.Extensions.DependencyInjection --version 6.0.0
  1. 新建类OrderItem

    public class OrderItem
    {
    public string Name { get; set; } public decimal Price { get; set; } public int Number { get; set; } public OrderItem(string name, decimal price) : this(name, price, 1)
    { } public OrderItem(string name, decimal price, int number)
    {
    Name = name;
    Price = price;
    Number = number;
    }
    }
  2. 新建类Order

    public class Order
    {
    public string Name { get; set; } public decimal TotalPrice { get; set; } public List<OrderItem> OrderItems { get; set; } public Order(string name)
    {
    Name = name;
    } public Order(string name, OrderItem orderItem) : this(name)
    {
    OrderItems = new List<OrderItem> { orderItem };
    TotalPrice = OrderItems.Sum(item => item.Price * item.Number);
    }
    }
  3. 修改类Program

    using Assignment.Masa.Mapster.Domain.Aggregate;
    using Masa.BuildingBlocks.Data.Mapping;
    using Masa.Contrib.Data.Mapping.Mapster;
    using Microsoft.Extensions.DependencyInjection; Console.WriteLine("Hello Masa Mapster!"); IServiceCollection services = new ServiceCollection();
    services.AddMapping(); var request = new
    {
    Name = "Teach you to learn Dapr ……",
    OrderItem = new OrderItem("Teach you to learn Dapr hand by hand", 49.9m)
    };
    var serviceProvider = services.BuildServiceProvider();
    var mapper = serviceProvider.GetRequiredService<IMapper>();
    var order = mapper.Map<Order>(request); Console.WriteLine($"{nameof(Order.TotalPrice)} is {order.TotalPrice}");//控制台输出49.9 Console.ReadKey();

如果转换成功,TotalPrice的值应该是49.9,那么我们运行控制台程序来验证转换是否成功:

如何实现

上面我们提到了Masa.Contrib.Data.Mapping.Mapster可以自动获取并使用最佳构造函数映射,进而完成对象到对象的映射,那么它是如何实现的呢?会不会对性能有什么影响呢?

做到自动获取并使用最佳构造函数映射是使用的Mapster提供的构造函数映射的功能,通过指定构造函数,完成对象到对象的映射。

查看文档

总结

目前Masa.Contrib.Data.Mapping.Mapster的功能相对较弱,当前版本与Mapster的相比仅仅增加了一个自动获取并使用最佳构造函数的功能,让我们在面对无空构造函数且拥有多个构造函数的类时也能轻松的完成映射,不需要额外多写一行代码。

但我觉得Masa版的Mapping最大的好处是项目依赖的是BuildingBlocks下的IMapper,而不是Mapster,这也就使得我们的项目与具体的映射器实现脱离,如果我们被要求项目必须要使用AutoMapper,只需要实现AutoMapper版的IMapper即可,无需更改太多的业务代码,仅需要更换一下引用的包即可,这也是BuildingBlocks的魅力所在

本章源码

Assignment04

https://github.com/zhenlei520/MasaFramework.Practice

开源地址

MASA.BuildingBlocks:https://github.com/masastack/MASA.BuildingBlocks

MASA.Contrib:https://github.com/masastack/MASA.Contrib

MASA.Utils:https://github.com/masastack/MASA.Utils

MASA.EShop:https://github.com/masalabs/MASA.EShop

MASA.Blazor:https://github.com/BlazorComponent/MASA.Blazor

如果你对我们的 MASA Framework 感兴趣,无论是代码贡献、使用、提 Issue,欢迎联系我们

对象映射 - Mapping.Mapster的更多相关文章

  1. [非专业翻译] 高性能对象映射框架 - Mapster

    [非专业翻译] 高性能对象映射框架 - Mapster 系列介绍 [非专业翻译] 是对没有中文文档进行翻译的系列博客,文章由机翻和译者自己理解构成,和原文相比有所有不通,但意思基本一致. 因个人能力有 ...

  2. 一:ORM关系对象映射(Object Relational Mapping,简称ORM)

    狼来的日子里! 奋发博取 10)django-ORM(创建,字段类型,字段参数) 一:ORM关系对象映射(Object Relational Mapping,简称ORM) ORM分两种: DB fir ...

  3. 基于JsonPath和XmlPath的对象映射(Object Mapping)

    rest-assured支持映射Java对象到Json和XML以及从Json和XML中映射到Java对象.Json映射需要在classpath 中有Jackson.Jackson 2或者是Gson,X ...

  4. 【5min+】 对象映射只有AutoMapper?试试Mapster

    系列介绍 [五分钟的dotnet]是一个利用您的碎片化时间来学习和丰富.net知识的博文系列.它所包含了.net体系中可能会涉及到的方方面面,比如C#的小细节,AspnetCore,微服务中的.net ...

  5. KnockoutJS 3.X API 第八章 映射(mapping)插件

    Knockout旨在允许您将任意JavaScript对象用作视图模型. 只要一些视图模型的属性是observables,您可以使用KO将它们绑定到您的UI,并且UI将在可观察属性更改时自动更新. 大多 ...

  6. php设计模式 数据对象映射模式

    数据对象映射模式,是将对象和数据存储映射起来,对一个对象的操作会映射为对数据存储的操作. 在代码中实现数据对象映射模式,实现一个ORM类,将复杂的sql语句映射成对象属性的操作.对象关系映射(Obje ...

  7. PHP 设计模式 笔记与总结(9)数据对象映射模式

    [数据对象映射模式] 是将对象和数据存储映射起来,对一个对象的操作会映射为对数据存储的操作.例如在代码中 new 一个对象,使用数据对象映射模式就可以将对象的一些操作比如设置一些属性,就会自动保存到数 ...

  8. .NET平台开源项目速览-最快的对象映射组件Tiny Mapper之项目实践

    心情小札:近期换了工作,苦逼于22:00后下班,房间一篇狼藉~ 小翠鄙视到:"你就适合生活在垃圾堆中!!!" 晚上浏览博客园 看到一篇非常实用的博客:.NET平台开源项目速览(14 ...

  9. 关于iOS上的对象映射公用方法-备

    具体的使用方法,请见下面说明,或者见工程里的单元测试代码.或者,参考原始文档: https://github.com/mystcolor/JTObjectMapping 使用方法 ======== 绝 ...

随机推荐

  1. 删库到跑路?还得看这篇Redis数据库持久化与企业容灾备份恢复实战指南

    本章目录 0x00 数据持久化 1.RDB 方式 2.AOF 方式 如何抉择 RDB OR AOF? 0x01 备份容灾 一.备份 1.手动备份redis数据库 2.迁移Redis指定db-数据库 3 ...

  2. 2021.07.23 P2474 天平(差分约束)

    2021.07.23 P2474 天平(差分约束) [P2474 SCOI2008]天平 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题意: 已知A,B和每两个点点权,求点权i, ...

  3. Python求解线性规划——PuLP使用教程

    简洁是智慧的灵魂,冗长是肤浅的藻饰.--莎士比亚<哈姆雷特> 1 PuLP 库的安装 如果您使用的是 Anaconda[1] 的话(事实上我也更推荐这样做),需要先激活你想要安装的虚拟环境 ...

  4. Incorrect table definition; there can be only one TIMESTAMP column with CURRENT_TIMESTAMP in DEFAULT or ON UPDATE clause

    错误描述 在DBeaver执行DDL语句时报错:SQL 错误 [1293] [HY000]: Incorrect table definition; there can be only one TIM ...

  5. fedora使用root超级用户

    sudo -i可以使当前用户变成root帐号. 这样就不用一遍一遍的输sudo 了! 原来用sudo su也可以.

  6. Python学习4(字典的内存分布)

    1.字典:是python数据类型之一,字典通过花括号来包含数据项,字典的每个元素由2个部分组成,键:值,字典是根据键来找对应的值. data = {"name": "Et ...

  7. 函数式接口和@FunctionalInterface

    函数式接口的特点 接口有且仅有一个抽象方法 允许定义静态方法和默认方法(这两个都不是抽象方法) 允许java.lang.Object中的public方法(因为任何一个函数式接口的实现,默认都继承了Ob ...

  8. Spring Cloud Feign+Hystrix自定义异常处理

    开启Hystrix spring-cloud-dependencies Dalston版本之后,默认Feign对Hystrix的支持默认是关闭的,需要手动开启. feign.hystrix.enabl ...

  9. windbg的时间旅行实现对 C# 程序的终极调试

    一:什么是时间旅行 简而言之就是把程序的执行流拍成vlog,这样就可以对 vlog 快进或者倒退,还可以分享给别人做进一步的分析,是不是想都不敢想. 很开心的是 windbg preview 版本中已 ...

  10. VMware服务关闭后一定要重启

    重要的事情说三遍:服务暂时关闭记得重启,服务暂时关闭记得重启,服务暂时关闭记得重启!!! VMware服务由于安装补丁的需要我暂时把服务关闭了,于是我遇到了尴尬的一幕,于是乎发现上不了网了,于是各种操 ...