前言

在我的项目中咱们会常常遇到对象的映射,比方像Model和Dto之间的映射,或者是对象的深拷贝,这些都是须要咱们本人实现的。此时,我的项目中会呈现很多初始化对象的代码,这些代码写起来相当的枯燥乏味,那么有没有什么方法加重咱们的工作量,使得咱们能够把工夫破费到业务性能上呢?

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

  • Mapster
  • AutoMapper

说到对象映射框架,大家想到的最多的是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.OpenConsole.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.4dotnet 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.9Console.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...

开源地址

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

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

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

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

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

如果你对咱们的 MASA Framework 感兴趣,无论是代码奉献、应用、提 Issue,欢送分割咱们