前言
在我的项目中咱们会常常遇到对象的映射,比方像 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
映射到新对象
-
新建类
UserDto
public class UserDto {public int Id { get; set;} public string Name {get; set;} public uint Gender {get; set;} public DateTime BirthDay {get; set;} }
-
新建一个匿名对象,作为待转换的对象源
var user = new { Id = 1, Name = "Tom", Gender = 1, BirthDay = DateTime.Parse("2002-01-01") };
-
将 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 的按需查找,例如:
-
新建类
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);// 应用内存数据库,不便测试 } }
-
新建类
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;} }
-
应用基于 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.Mapster
,Microsoft.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
-
新建类
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; } }
-
新建类
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); } }
-
批改类
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…
开源地址
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,欢送分割咱们