AutoMapper是一个对象-对象映射器。对象-对象映射通过将一种类型的输入对象转换为另一种类型的输出对象来工作。 基于命名约定的对象到对象的映射工具,只要2个对象的属性具有相同的名字(或者符合它规定的命名规定),AutoMapper就可以帮我们自动在2个对象间进行属性值的映射。
简单来说:AutoMapper 是一个对象-对象映射器,可以将一个对象映射到另一个对象。
如果不符合约定的属性,就需要自定义映射行为,需要告诉AutoMapper,在使用Map进行映射之前,必须使用CreateMap()进行配置
注意:将源映射到目标时,AutoMapper 将忽略空引用异常,可以通过自定义解析器来更改这种设置。
- Source Object:映射的原始对象
- Destination Object:需要映射到的新对象
- AutoMapper:执行实际映射工作的框架
- Configuration: 在使用
AutoMapper之前,您需要配置映射规则。这通常在应用程序的启动代码中完成。
public class Foo
{
public int ID { get; set; }
public string Name { get; set; }
}
public class FooDto
{
public int ID { get; set; }
public string Name { get; set; }
}
注意:经验法则是一个应用程序域AppDomian只需要一个AutoMapper配置对象,简单来说就是Startup.cs 文件的 ConfigureServices 方法中,或在控制台应用的 Main 方法中)创建和配置一个 MapperConfiguration 实例,并将这个实例在应用程序的其他部分中重用
MapperConfiguration 配置映射规则
public void Map()
{
//使用Map方法之前,首先要告诉AutoMapper是从什么类映射到什么类
//Foo 类型的对象转换为 FooDto 类型的对象。
var config = new MapperConfiguration(cfg => cfg.CreateMap<Foo, FooDto>());
var mapper = config.CreateMapper();
//进行对象的转换
Foo foo = new Foo { ID = 1, Name = "Tom" };
FooDto dto = mapper.Map<FooDto>(foo);
}Profile 是组织映射的另一种方式。新建一个类,继承 Profile,并在构造函数中配置映射。
//Mappings文件下的LoginMapping
public class LoginMapping : Profile
{
public LoginMapping()
{
CreateMap<UserAccount, LoginDto>();
}
}
//注册
var config = new MapperConfiguration(cfg =>
{
//将LoginMapping添加到MapperConfiguration的配置中
cfg.AddProfile<LoginMapping>();
//或者cfg.AddProfile(new LoginMapping());
});将映射关系添加到Profile,再加载Profile,类似于模块化分割业务,让项目结构更加清晰
var config = new MapperConfiguration(cfg =>
{
// 扫描当前程序集
cfg.AddMaps(System.AppDomain.CurrentDomain.GetAssemblies());
// 也可以传程序集名称(dll 名称)
cfg.AddMaps("LibCoreTest");
});AutoMapper 也可以在指定的程序集中扫描从 Profile 继承的类,并将其添加到配置中。
默认情况下,AutoMapper 基于相同的字段名映射,并且是 不区分大小写 的。
-
SourceMemberNamingConvention表示源类型命名规则 (蛇形命名法) -
DestinationMemberNamingConvention表示目标类型命名规则 (驼峰命名法)
需要指定命名规则,使其能正确映射。
默认情况下,AutoMapper 仅映射 public 成员,但其实它是可以映射到 private 属性的。
var config = new MapperConfiguration(cfg =>
{
cfg.ShouldMapProperty = p => p.GetMethod.IsPublic || p.SetMethod.IsPrivate;
cfg.CreateMap<Source, Destination>();
});AutoMapper 尝试映射每个公共属性/字段。以下配置将忽略字段映射。
var config = new MapperConfiguration(cfg =>
{
cfg.ShouldMapField = fi => false;
});var config = new MapperConfiguration(cfg =>
{
cfg.ReplaceMemberName("Ä", "A");
});var configuration = new MapperConfiguration(cfg =>
{
cfg.RecognizePrefixes("before");//前缀
cfg.RecognizePostfixes("after");//后缀
cfg.CreateMap<Src03,Dest03>();
});
var mapper = configuration.CreateMapper();
var dest = mapper.Map<Dest03>(new Src03() { Nameafter = "zhangsan", beforeAge = 18 });cfg.ClearPrefixes();//清除所有前缀//ShouldMapField设置字段映射范围,ShouldMapProperty设置属性范围
var configuration = new MapperConfiguration(cfg => {
cfg.ShouldMapField = fi => false;
cfg.ShouldMapProperty = pi => pi.GetMethod != null && (pi.GetMethod.IsPublic || pi.GetMethod.IsPrivate);
});有些类,属性的 set 方法是私有的。
public class Commodity
{
public string Name { get; set; }
public int Price { get; set; }
}
public class CommodityDto
{
public string Name { get; }
public int Price { get; }
public CommodityDto(string name, int price)
{
Name = name;
Price = price * 2;//映射后 Price会乘 2。
}
}AutoMapper 会自动找到相应的构造函数调用。如果在构造函数中对参数做一些改变的话,其改变会反应在映射结果中。
禁用构造函数映射的话,目标类要有一个无参构造函数。
var config = new MapperConfiguration(cfg => cfg.DisableConstructorMapping());public class Source
{
public int Value { get; set; }
public int Percent { get; set; }
}
public class Destination
{
public int Value { get; set; }
}
var sources = new[]
{
new Source { Value = 5, Percent = 50 },
new Source { Value = 6, Percent = 50 },
new Source { Value = 7, Percent = 50 }
};
var iEnumerableDest = mapper.Map<Source[], IEnumerable<Destination>>(sources);
var iCollectionDest = mapper.Map<Source[], ICollection<Destination>>(sources);
var iListDest = mapper.Map<Source[], IList<Destination>>(sources);
var listDest = mapper.Map<Source[], List<Destination>>(sources);
var arrayDest = mapper.Map<Source[], Destination[]>(sources);支持的源集合类型包括:
-
IEnumerable -
IEnumerable<T> -
ICollection -
ICollection<T> -
IList -
IList<T> -
List<T> -
Arrays
假如某个成员的名称为NameAAA,则名为NameAAA的field,与名为NameAAA的property,与名为GetNameAAA的方法,三者之间可以自动相互映射
var configuration = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Dest01, Src01>();
});
var mapper = configuration.CreateMapper();
var dest = mapper.Map<Dest01>(new Src01() { Name = "zhangsan", Age = 18 });同一个字段的映射,后面的会覆盖前面的,不同的字段,没有做映射的,不会进行赋值
var configuration = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Src02, Dest02>()
.ForMember(dest => dest.NameDest, opt => opt.MapFrom(src => src.NameSrc));
});
var mapper = configuration.CreateMapper();
var dest = mapper.Map<Dest02>(new Src02() { NameSrc = "zhangsan" });类内部嵌套一个类,需要将嵌套的类也进行映射
public class SrcOuter
{
public string OutName { get; set; }
public int OutAge { get; set; }
public SrcInner Inner { get; set; }
}
public class SrcInner
{
public string Name { get; set; }
public int Age { get; set; }
}
public class DestOuter
{
public string OutName { get; set; }
public int OutAge { get; set; }
public DestInner Inner { get; set; }
}
public class DestInner
{
public string Name { get; set; }
public int Age { get; set; }
}
var configuration = new MapperConfiguration(cfg =>
{
cfg.CreateMap<SrcOuter, DestOuter>();
cfg.CreateMap<SrcInner, DestInner>();
});
var mapper = configuration.CreateMapper();
var dest = mapper.Map<DestOuter>(new SrcOuter(){OutName = "zhangsan",OutAge = 18,Inner = new SrcInner() { Name = "lisi", Age = 20 }});符合某些条件时才映射Condition方法会在MapFrom方法后判断,PreCondition会在MapFrom前判断。
var configuration = new MapperConfiguration(cfg =>
{
cfg.CreateMap<SrcCondition, DestCondition>()
//src.Name.Length>=3&&(src.Name+"XXX").Length >= 5 两个条件都满足才映射
.ForMember(dest => dest.Name, opt => opt.PreCondition(src => src.Name.Length >= 3))
.ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Name + "XXX"))
.ForMember(dest => dest.Name, opt => opt.Condition(src => src.Name.Length >= 5))
//src.Age <= 15&&src.Age * 3>=30 两个条件都满足才映射
.ForMember(dest => dest.Age, opt => opt.PreCondition(src => src.Age <= 15))
.ForMember(dest => dest.Age, opt => opt.MapFrom(src => src.Age * 3))
.ForMember(dest => dest.Age, opt => opt.Condition(src => src.Age >= 30));
});
var mapper = configuration.CreateMapper();
var dest = mapper.Map<DestCondition>(new SrcCondition() { Name = "zhangsan", Age = 18 });//给个默认值
cfg.CreateMap<Src01, Dest01>()
.ForMember(dest => dest.Name, opt => opt.NullSubstitute("XXX"));ReverseMap一般在CreateMap方法或者ForMember等方法之后,相当于src和dest根据你自己的配置反向映射
cfg.CreateMap<Order, OrderDto>().ReverseMap();
//等同于以下两句
cfg.CreateMap<Order,OrderDto>();
cfg.CreateMap<OrderDto,Order>();
//反向映射可以用ForPath配置
cfg.CreateMap<Order, OrderDto>()
.ForMember(d => d.CustomerName, opt => opt.MapFrom(src => src.Customer.Name))
.ReverseMap()
.ForPath(s => s.Customer.Name, opt => opt.MapFrom(src => src.CustomerName));VO(View Object):视图对象,用于展示层,它的作用是把某个指定页面(或组件)的所有数据封装起来。DTO(Data Transfer Object):数据传输对象,泛指用于展示层与服务层之间的数据传输对象。DO(Domain Object):领域对象,就是从现实世界中抽象出来的有形或无形的业务实体。PO(Persistent Object):持久化对象,它跟持久层(通常是关系型数据库)的数据结构形成一一对应的映射关系,如果持久层是关系型数据库,那么,数据表中的每个字段(或若干个)就对应PO的一个(或若干个)属性。DAO(Data Access Object):数据访问对象,主要用来封装对数据库的操作。
