Object Mapping- Mapping.Mapster

Object Mapping- Mapping.Mapster

In projects, we often encounter mappings of objects, such as mappings between Model and Dto, or deep copies of objects, which require us to implement ourselves.

最后更新 7/6/2022 8:18 PM
磊_磊
预计阅读 8 分钟
分类
.NET
标签
.NET C#

preface

In projects, we often encounter object mappings, such as the mapping between Model and Dto, or deep copies of objects, which require us to implement ourselves. At this time, there will be a lot of code to initialize objects in the project, which is quite boring to write. So is there any way to reduce our workload so that we can spend our time on business functions?

At present, among the object mapping frameworks in. Net, powerful and excellent object mapping frameworks already exist. Among them, the most used ones are:

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

Introduction to Mapster

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

preparations

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

dotnet add package Mapster --version 7.3.0

###Map to a new object

  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; }
}
  1. Create a new anonymous object as the source of the object to be converted
var user = new
{
    Id = 1,
    Name = "Tom",
    Gender = 1,
    BirthDay = DateTime.Parse("2002-01-01")
};
  1. Map user source object to target object (UserDto)
var userDto = user.Adapt<UserDto>();
Console.WriteLine($"映射到新对象,Name: {userDto.Name}");

Run the console program to verify that the conversion was successful:

映射到新对象

data type

In addition to providing object-to-object mapping, it also supports data type conversions, such as:

** Basic type **

  • Provides type mapping functionality, similar to Convert.ChangeType()
string res = "123";
decimal i = res.Adapt<decimal>(); //equal to (decimal)123;
Console.WriteLine($"结果为:{i == int.Parse(res)}");

Run the console program:

基本类型转换

** Enumeration types **

  • Mapping enumerations to numeric types also supports string-to-enumerations and enumeration-to-string mappings, twice as fast as the default implementation of. NET
var fileMode = "Create, Open".Adapt<FileMode>();//等于 FileMode.Create | FileMode.Open
Console.WriteLine($"枚举类型转换的结果为:{fileMode == (FileMode.Create | FileMode.Open)}");

Run the console program to verify that the conversion was successful:

枚举类型转换

Queryable extension

Mapster provides an extension to Queryable to implement on-demand lookup of DbContext, for example:

  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);//使用内存数据库,方便测试
    }
}
  1. 新建类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;
    }
}
  1. 使用基于 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();
}

Run the console program to verify that the conversion was successful:

Queryable扩展

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

What is Masa.Contrib.Data.Mapping.Mapster?

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

mapping rule

  • When the target object does not have a constructor: Use an empty constructor to map to fields and properties.

  • There are multiple constructors for the target object: Get the best constructor map

Best constructor: The number of parameters to the target object constructor is searched in descending order from large to small, the parameter names are consistent (case-insensitive), and the parameter types are consistent with the source object properties

preparations

新建一个控制台项目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;
    }
}
  1. 新建类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);
    }
}
  1. 修改类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();

If the conversion was successful, the value of TotalPrice should be 49.9, so we run the console program to verify that the conversion was successful:

Mapping.Mapster

how to achieve

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

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

查看文档

summary

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

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

Source code for this chapter

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

Open source address

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

If you are interested in our MASA Framework, whether it is code contribution, use, or issue presentation, please contact us

Keep Exploring

延伸阅读

更多文章
同分类 / 同标签 4/22/2026

Support for. NET by operating system versions (250707 update)

Use virtual machines and test machines to test the support of each version of the operating system for. NET. After installing the operating system, it is passed by measuring the corresponding running time of the installation and being able to run the Stardust Agent.

继续阅读
同分类 / 同标签 2/7/2026

Summary of experience in using AOT

From the very beginning of project creation, you should develop a good habit of conducting AOT release testing in a timely manner whenever new features are added or newer syntax is used.

继续阅读