腾讯老衲衲 發表於 2020-9-19 16:10:00

C# Mapster 对象映射器(C#对象映射器)

<h2 id="前言">前言</h2>
<p>谈到对象映射器,AutoMapper 知名度是非常的高,但很少有人知道 Mapster。而为什么选择 Mapster 呢?</p>
<p>理由一:性能优于 AutoMapper ,相关测试位于https://github.com/MapsterMapper/Mapster上查看。</p>
<p>理由二:多学习一项技能</p>
<p>网上查了一下,关于 Mapster 的资料非常少,所以在这里我们详细写下它的用法,以帮助更多的序员宝快速掌握它的用法。</p>
<p>官方文档:https://github.com/MapsterMapper/Mapster/wiki</p>
<h2 id="安装-mapster">安装 Mapster</h2>
<p><code>PM&gt; Install-Package Mapster</code> 或者 <code>dotnet add package Mapster</code></p>
<h2 id="定义实体">定义实体</h2>
<p>目的:使用 Mapster 实现 User 到 UserDto 的映射</p>
<pre><code class="language-C#">public class User
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Sex { get; set; }
    public string like { get; set; }
}

public class UserDto
{
    public string name { get; set; }
    public int UserAge { get; set; }
    public string UserSex { get; set; }
    public string like { get; set; }
}
</code></pre>
<h2 id="简单使用">简单使用</h2>
<pre><code class="language-C#">/*
* 默认情况下,无需任何配置,Mapster会根据两个实体字段名称相同进行匹配
* 第一次调用时,配置会被缓存,第二次将会从缓存中取,以此提升性能
*/
var user = new User();
var dto = user.Adapt&lt;UserDto&gt;();//映射为新对象
user.Adapt(dto);//在目标对象的基础上进行映射

//注意:Adapt扩展方法使用的配置为 `TypeAdapterConfig.GlobalSettings`
</code></pre>
<h2 id="mapster-配置-typeadapterconfig">Mapster 配置 (TypeAdapterConfig)</h2>
<p>可以直接使用 Mapster 内置的全局静态配置 <code>TypeAdapterConfig.GlobalSettings</code>,也可以实例化一个配置 <code>new TypeAdapterConfig()</code></p>
<p>推荐使用实例化的方式,对 TypeAdapterConfig 进行映射配置。</p>
<blockquote>
<p>注:Mapster 默认匹配规则是相同字段名之间进行映射。</p>
</blockquote>
<h3 id="方式一">方式一</h3>
<p>直接在 TypeAdapterConfig 配置对象的映射关系</p>
<pre><code class="language-C#">var config = new TypeAdapterConfig();
//映射规则
config.ForType&lt;User, UserDto&gt;()
    .Map(dest =&gt; dest.UserAge, src =&gt; src.Age)
    .Map(dest =&gt; dest.UserSex, src =&gt; src.Sex);

var mapper = new Mapper(config);//务必将mapper设为单实例

var user = new User{Name = "xiaowang",Age = 18,Sex = "boy"};
var dto = mapper.Map&lt;UserDto&gt;(user);
</code></pre>
<p>字段带有前后缀,可以使用 <code>NameMatchingStrategy.ConvertDestinationMemberName</code> 对目标字段名称进行替换,使得它和源字段名称相同。<br>
还有替换源字段的方法<code>NameMatchingStrategy.ConvertSourceMemberName</code></p>
<blockquote>
<p>注意:如果一个<code>ForType</code>定义多个<code>NameMatchingStrategy</code>,后定义的会覆盖先定义的规则,所以只有最后定义的规则会生效</p>
</blockquote>
<pre><code class="language-C#">var config = new TypeAdapterConfig();
//使用
config.ForType&lt;User, UserDto&gt;()
    .NameMatchingStrategy(NameMatchingStrategy.ConvertDestinationMemberName(dest =&gt; dest.Replace("User", "")));
</code></pre>
<h3 id="方式二">方式二</h3>
<p>使用接口的方式,需要实现 <code>IRegister</code></p>
<pre><code class="language-C#">//实现接口 IRegister
public class UserDtoRegister : IRegister
{
    public void Register(TypeAdapterConfig config)
    {
      config.ForType&lt;User,UserDto&gt;()
            Map(dest =&gt; dest.UserAge, src =&gt; src.Age);
            //...
    }
}

//实例化Mapper
var config = new TypeAdapterConfig();
//var config = TypeAdapterConfig.GlobalSettings;
//只有要给定 IRegister 所在的程序集名称,Mapster 会自动识别 IRegister,进行配置注入。
config.Scan("程序集名称1","程序集名称2");
var mapper = new Mapper(config);//务必设置为单实例
</code></pre>
<h2 id="忽略字段">忽略字段</h2>
<pre><code class="language-C#">var config = new TypeAdapterConfig();
//映射规则
config.ForType&lt;User, UserDto&gt;()
    .Map(dest =&gt; dest.UserAge, src =&gt; src.Age)
    .Map(dest =&gt; dest.UserSex, src =&gt; src.Sex);
    .IgnoreNullValues(true)//忽略空值映射
    .Ignore(dest =&gt; dest.UserAge)//忽略指定字段
    .IgnoreAttribute(typeof(DataMemberAttribute))//忽略指定特性的字段
    .NameMatchingStrategy(NameMatchingStrategy.IgnoreCase)//忽略字段名称的大小写
    .IgnoreNonMapped(true);//忽略除以上配置的所有字段

config.ForType&lt;User,UserDto&gt;()
    .IgnoreMember((member, side) =&gt; !member.Type.Namespace.StartsWith("System"));//实现更细致的忽略规则
</code></pre>
<p><code>member</code>和<code>side</code>分别对应<code>IMemberModel</code>和<code>MemberSide</code>,这里我贴出相应的源码。</p>
<pre><code class="language-C#">//包含了映射类型的信息
public interface IMemberModel
{
    Type Type { get; }
    string Name { get; }
    object? Info { get; }
    AccessModifier SetterModifier { get; }
    AccessModifier AccessModifier { get; }

    IEnumerable&lt;object&gt; GetCustomAttributes(bool inherit);
}
//标识当前是源类型还是目标类型
public enum MemberSide
{
    Source = 0,
    Destination = 1
}
</code></pre>
<h2 id="分支fork">分支(Fork)</h2>
<p>Mapster 的 Fork 功能允许我们定义局部的映射规则,并且分支不会重复编译,不需要考虑性能问题。</p>
<pre><code class="language-C#">var config = new TypeAdapterConfig();
var mapper = new Mapper(config);

var user = new User{Name = "xiaowang",Age = 18,Sex = "boy"};

var dto = mapper.From(user).ForkConfig(forked =&gt;
    {
      //该分支规则,不会重复编译,仅限该语句中有效,不会影响config的配置
      forked.ForType&lt;User, UserDto&gt;().Map(dest =&gt; dest.name, src =&gt; src.Name);
    })
    .AdaptToType&lt;UserDto&gt;();//映射为新对象

dto = mapper.From(user).ForkConfig(forked =&gt;
    {
      forked.ForType&lt;User, UserDto&gt;().Map(dest =&gt; dest.name, src =&gt; src.Name);
    })
    .AdaptTo(new UserDto());//在目标对象基础上进行映射
</code></pre>
<h2 id="newconfig-方法">NewConfig 方法</h2>
<p>NewConfig 方法允许我们对两个类型之间新建配置,如果两个类型之前配置了映射关系,则 NewConfig 方法会覆盖之前的配置</p>
<pre><code class="language-C#">var config = new TypeAdapterConfig();
config.ForType&lt;User,UserDto&gt;().Map(dest =&gt; dest.UserAge, src =&gt; src.Age);
//...

//覆盖 User 和 UserDto 之前的配置
config.NewConfig&lt;User,UserDto&gt;().Map(dest=&gt;dest.UserAge,src=&gt;100);

//扩展知识:覆盖Mapster默认静态配置
TypeAdapterConfig&lt;User,UserDto&gt;.NewConfig().Default.NameMatchingStrategy(NameMatchingStrategy.IgnoreCase);
</code></pre>
<h2 id="运行时传参">运行时传参</h2>
<p>允许运行时传入数据,干预映射过程</p>
<pre><code class="language-C#">var config = new TypeAdapterConfig();
config.ForType&lt;User, UserDto&gt;()
    .Map(dest =&gt; dest.name, src =&gt; MapContext.Current.Parameters["userName"]);//配置运行时参数
var mapper = new Mapper(config);

//使用时传入数据
var user = new User();
var dto = mapper.From(user).BuildAdapter().AddParameters("userName","xiaowang").AdaptToType&lt;UserDto&gt;();
</code></pre>
<h2 id="其他知识点">其他知识点</h2>
<p>我们尽量不要把实体间的映射规则配置到 <code>TypeAdapterConfig.GlobalSettings</code> (默认配置)。随着业务的发展,一个配置很难兼顾所有业务,可能会出行冲突的情况,相对复杂的业务,可以新建一个<code>TypeAdapterConfig</code>,或者使用 <code>config.Clone()</code>能轻松复制一份配置。全局配置可以放一些简单的配置项,例如:映射时忽略大小写。</p>
<blockquote>
<p>注意:Adapt 扩展方法使用的是 <code>TypeAdapterConfig.GlobalSettings</code></p>
</blockquote>
<h2 id="小技巧">小技巧</h2>
<h3 id="adapt-扩展方法的使用">Adapt 扩展方法的使用</h3>
<pre><code class="language-C#">Dictionary&lt;string,object&gt; dict = new User().Adapt&lt;Dictionary&lt;string,object&gt;&gt;();//object 到 Dictionary 的转换

string s = 123.Adapt&lt;string&gt;(); //equal to 123.ToString();

int i = "123".Adapt&lt;int&gt;();//equal to int.Parse("123");
</code></pre>
<h3 id="拦截映射前后">拦截映射前后</h3>
<p><code>BeforeMapping</code> 方法和 <code>AfterMapping</code></p>
<pre><code class="language-C#">//BeforeMapping 映射执行前
config.ForType&lt;User, UserDto&gt;().BeforeMapping((src, dest) =&gt;
      {
            src.Age = 100;
            dest.UserAge = src.Age + 10;
      });

//AfterMapping 映射执行后
//...
</code></pre>
<p>更多的使用技巧可以查看官方文档:https://github.com/MapsterMapper/Mapster/wiki</p><br><br>
来源:https://www.cnblogs.com/qiqigou/p/13696669.html
頁: [1]
查看完整版本: C# Mapster 对象映射器(C#对象映射器)