ASP.NET Core 中的對象映射之 AutoMapper

AutoMapper 簡介

AutoMapper是一個對象映射器,它能夠將一種類型的對象轉換爲另外一種類型的對象。git

它提供了映射規則及操做方法,使咱們不用過多配置就能夠映射兩個類, 能夠幫咱們免於編寫無聊的映射代碼. 在代碼層與層之間隔離模型model上很是有用.github

AutoMapper 使用

初始化

建立兩個簡單的類用於測試:app

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可使用靜態類和實例方法來建立映射.ide

  • 靜態類方式測試

    Mapper.Initialize(cfg => cfg.CreateMap<UserEntity, UserDTO>());
    var userDTO = Mapper.Map<UserDTO>(user);
  • 實例方式ui

    var config = new MapperConfiguration(cfg => cfg.CreateMap<UserEntity, UserDTO>());
    var mapper = config.CreateMapper();
    
    var userDTO = mapper.Map<UserDTO>(user);
  • 依賴注入code

    使用擴展 AutoMapper.Extensions.Microsoft.DependencyInjection 來實現AutoMapper的依賴注入. 本質是註冊一個MapperConfiguration的單例和IMapper的scope實例, 經過程序集掃描添加AutoMapper的相關配置和映射.orm

    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添加.htm

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>());
    }
}

參考

相關文章
相關標籤/搜索