在分解复杂的软件系统时,软件设计者用得最多的技术之一就是分层。分层右很多优点:如提高复用性、较低耦合性,但也有其缺陷所在:如过多的层次会影响性能、有时会为我们带来级联修改。
会带来级联修改怎么理解呢?例如需求变化要增加一个在用户界面上显示的数据域,就必须在数据中增加相应字段,还必须在用户界面和数据库之间的每一层做相应的修改。在不同层次中会有不同的数据实体类(有些类还包括一些方法),这些实体类之间为了实现数据传递就必须进行类之前的转换,这样的转换可以手写代码进行一对一的转换完成任务,就像下图一样,并且你还得为对应的List<T>类书写相应的转换方法,有木有更简单方便的方案呢?这就要说到标题里写的AutoMapper,从名字可以看出这是一款用于映射的工具。
直接上代码,第一先定义需要进行转换的实体类,第二步是继承Profile接口,进行注册。
using AutoMapper;
namespace WindowsFormsApp1
{
/// <summary>
/// 模仿需要转换的数据实体类
/// </summary>
public class Source
{
public int SomeValue { get; set; }
public string AnotherValue { get; set; }
}
/// <summary>
/// 模仿需要转换的数据实体类
/// </summary>
public class Destination
{
public int SomeValue { get; set; }
}
/// <summary>
/// 通过Profile注册
/// 告诉AutoMapper需要进行哪些类的转换,如果A转换成B,B也要转换成A,那就需要写两条
/// </summary>
public class SourceProfile : Profile
{
public SourceProfile()
{
CreateMap<Source, Destination>();
CreateMap<Destination, Source>();
}
}
}
第三步是将Profile中书写的映射关系添加到Mapper实例,常规需要书写一个方法用于获取唯一的Mapper实例(单例模式),实例中配置了映射关系。
using AutoMapper;
namespace WindowsFormsApp1
{
public class AutoMapperConfigure
{
public static IMapper GetMapper()
{
var config = new MapperConfiguration(cfg =>
{
// 扫描当前程序集
cfg.AddMaps(System.AppDomain.CurrentDomain.GetAssemblies());
});
var mapper = config.CreateMapper();
return mapper;
}
}
}
第四步具体调用,默认情况下,AutoMapper 基于相同的字段名映射,并且是 不区分大小写 的。也可以通过修改配置,实现AutoMapper提供的命名规则映射。LowerUnderscoreNamingConvention
和 PascalCaseNamingConvention
是 AutoMapper 提供的两个命名规则。前者命名是小写并包含下划线,后者就是帕斯卡命名规则(每个单词的首字母大写)。
private void promptMessageButton_Click(object sender, EventArgs e)
{
Source source = new Source()
{
SomeValue = 1,
AnotherValue = "Another"
};
var mapper = AutoMapperConfigure.GetMapper();
Destination destination = mapper.Map<Destination>(source);
MessageBox.Show(destination.SomeValue.ToString());
}
像最开始截图那种比较复杂的涉及枚举和枚举描述之间转换,甚至List<string>转换成string,也可以用AutoMapper实现。可以对转换的成员属性调用写好的静态方法,来实现按规则的准换,如下图。
参考资料:
有兴趣的同学还可以看一下官方文档,还有样例:https://docs.automapper.org/en/latest/