ASP.NET Core 中的物件對映之 AutoMapper
目錄
AutoMapper 簡介
AutoMapper是一個物件對映器,它可以將一種型別的物件轉換為另一種型別的物件。
它提供了對映規則及操作方法,使我們不用過多配置就可以對映兩個類, 可以幫我們免於編寫無聊的對映程式碼. 在程式碼層與層之間隔離模型model上非常有用.
AutoMapper 使用
初始化
建立兩個簡單的類用於測試:
public class UserEntity
{
public int Id { get; set; }
public string Name { get; set; }
}
public class UserDTO
{
public int Id { get; set; }
public string Name { get; set; }
}
AutoMapper可以使用靜態類和例項方法來建立對映.
靜態類方式
Mapper.Initialize(cfg => cfg.CreateMap<UserEntity, UserDTO>()); var userDTO = Mapper.Map<UserDTO>(user);
例項方式
var config = new MapperConfiguration(cfg => cfg.CreateMap<UserEntity, UserDTO>()); var mapper = config.CreateMapper(); var userDTO = mapper.Map<UserDTO>(user);
依賴注入
使用擴充套件 AutoMapper.Extensions.Microsoft.DependencyInjection 來實現AutoMapper的依賴注入. 本質是註冊一個MapperConfiguration的單例和IMapper的scope例項, 通過程式集掃描新增AutoMapper的相關配置和對映.
IServiceCollection services = new ServiceCollection(); services.AddAutoMapper(); var provider = services.BuildServiceProvider(); using (var scope = provider.CreateScope()) { var mapper = scope.ServiceProvider.GetService<IMapper>(); var userDTO = mapper.Map<UserDTO>(user); }
Profile設定
可以使用Profie配置來實現對映關係, 然後通過AddProfile新增.
public class UserProfile : Profile
{
public UserProfile()
{
CreateMap<UserEntity, UserDTO>();
}
}
var config = new MapperConfiguration(cfg => cfg.AddProfile<UserProfile>());
扁平化對映
AutoMapper支援扁平化對映, 它會根據Pascal命名方式分割目標欄位為單個單詞, 可自動對映屬性名+內嵌屬性名. 如下例AutoMapper自動對映UserEntity.Address.City -> UserDTO.AddressCity。
public class UserEntity
{
public int Id { get; set; }
public string Name { get; set; }
public Address Address { get; set; }
}
public class Address
{
public string City { get; set; }
public string Country { get; set; }
}
public class UserDTO
{
public int Id { get; set; }
public string Name { get; set; }
public string AddressCity { get; set; }
public string AddressCountry { get; set; }
}
集合對映
AutoMapper除了可以對映單個物件外,也可以對映集合物件。
CreateMap<UserEntity, UserDTO>();
var userList = new List<UserEntity> {
new UserEntity { Id = 1, Name="Test1" },
new UserEntity { Id = 2, Name="Test2" },
};
var dtoList = mapper.Map<List<UserDTO>>(userList);
public class UserEntity
{
public int Id { get; set; }
public string Name { get; set; }
public List<AddressEntity> AddressList { get; set; }
}
public class AddressEntity
{
public string City { get; set; }
public string Country { get; set; }
}
public class UserDTO
{
public int Id { get; set; }
public string Name { get; set; }
public List<AddressDTO> AddressList { get; set; }
}
public class AddressDTO
{
public string City { get; set; }
public string Country { get; set; }
}
CreateMap<AddressEntity, AddressDTO>();
CreateMap<UserEntity, UserDTO>();
var user = new UserEntity
{
Id = 1,
Name = "Test",
AddressList = new List<AddressEntity>
{
new AddressEntity { City = "ShangHai", Country = "China"},
new AddressEntity { City = "BeiJing", Country = "China"}
}
};
var userDTO = mapper.Map<UserDTO>(user);
投影
當把一個源值投影到一個不精準匹配源結構的目標值時,使用MapFrom指明成員對映定義。
public class UserEntity
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime BirthDate { get; set; }
}
public class UserDTO
{
public int Id { get; set; }
public string Name { get; set; }
public string BirthYear { get; set; }
public string BirthMonth { get; set; }
}
public class UserProfile : Profile
{
public UserProfile()
{
CreateMap<UserEntity, UserDTO>()
.ForMember(d => d.BirthYear, o => o.MapFrom(s => s.BirthDate.Year))
.ForMember(d => d.BirthMonth, o => o.MapFrom(s => s.BirthDate.Month));
}
}
var user = new UserEntity
{
Id = 1,
Name = "Test",
BirthDate = DateTime.Today,
};
var userDTO = mapper.Map<UserDTO>(user);
條件對映
有些情況下,我們將只滿足對映條件的才新增到屬性上.
public class UserProfile : Profile
{
public UserProfile()
{
CreateMap<UserEntity, UserDTO>()
.ForMember(d => d.Id, o => o.Condition(s => s.Id > 1));
}
}
值轉換
AutoMapper可以配置值轉換和空值替換
public class UserProfile : Profile
{
public UserProfile()
{
CreateMap<UserEntity, UserDTO>()
.ForMember(d => d.Name, o => o.NullSubstitute("Default Name"))
.ForMember(d => d.Name, o => o.AddTransform(val => string.Format("Name: {0}", val)));
}
}
設定轉換前後行為
有時候,在對映發生之前或之後,可能需要執行一些自定義的邏輯。
public class UserProfile : Profile
{
public UserProfile()
{
CreateMap<UserEntity, UserDTO>()
.BeforeMap((s, d) => s.BirthDate = s.BirthDate.AddYears(-12))
.AfterMap((s, d) => d.BirthMonth = "July");
}
}
配置驗證及設定
配置了對映,但是如何確定是否對映成功或者是否有欄位沒有對映呢?可以使用mapper.ConfigurationProvider.AssertConfigurationIsValid()來驗證是否對映成功。但也可以指定單個欄位不驗證.
public class UserProfile : Profile
{
public UserProfile()
{
CreateMap<UserEntity, UserDTO>()
.ForMember(d => d.NickName, o => o.Ignore());
}
}
反向對映
從6.1.0開始,AutoMapper通過ReverseMap可以實現反向對映。使用ReverseMap, 不用再建立DTO -> Entity的對映, 而且還能保留正向的對映規則。
public class UserProfile : Profile
{
public UserProfile()
{
CreateMap<UserEntity, UserDTO>()
.ReverseMap();
}
}
自定義轉換器
有些情況下目標欄位型別和源欄位型別不一致,可以通過型別轉換器實現對映,型別轉換器有三種實現方式:
void ConvertUsing(Func<TSource, TDestination> mappingFunction);
void ConvertUsing(ITypeConverter<TSource, TDestination> converter);
void ConvertUsing<TTypeConverter>() where TTypeConverter : ITypeConverter<TSource, TDestination>;
自定義解析器
某些情況下,解析規則會很複雜,使用自帶的解析規則無法實現。這時可以自定義解析規則,可以通過以下三種方式使用自定義的解析器:
ResolveUsing<TValueResolver>
ResolveUsing(typeof(CustomValueResolver))
ResolveUsing(aValueResolverInstance)
public class UserEntity
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class UserDTO
{
public string Name { get; set; }
}
public class UserNameResolver : IValueResolver<UserEntity, UserDTO, string>
{
public string Resolve(UserEntity source, UserDTO destination, string destMember, ResolutionContext context)
{
if (source != null && !string.IsNullOrEmpty(source.FirstName) && !string.IsNullOrEmpty(source.LastName))
{
return string.Format("{0} {1}", source.FirstName, source.LastName);
}
return string.Empty;
}
}
public class UserProfile : Profile
{
public UserProfile()
{
CreateMap<UserEntity, UserDTO>()
.ForMember(d => d.Name, o => o.ResolveUsing<UserNameResolver>());
}
}