ASP.NET Core-自動對映

流年sugar發表於2024-07-17

1.安裝NuGet包AutoMapper

2.建立示例Entity、Dto

Entity

 public class User 
 {
     public long Id { get; set; }
     /// <summary>
     /// 賬號
     /// </summary>
     public string UserName { get; set; }

     /// <summary>
     /// 名字
     /// </summary>
     public string Name { get; set; }

     public string Password { get; set; }

     /// <summary>
     /// 使用者是否鎖定
     /// </summary>
     public bool IsLockoutEnabled { get; set; }

     public string PhoneNumber { get; set; }

 }

Dto

 public class UserDto
 {
     public string UserName { get; set; }

     /// <summary>
     /// 名字
     /// </summary>
     public string Name { get; set; }

     public string Password { get; set; }

     /// <summary>
     /// 使用者是否鎖定
     /// </summary>
     [PropertyMapper(SourceName = "IsLockoutEnabled")]
     public bool IsLock { get; set; }

     public string PhoneNumber { get; set; }
 }

3.配置對映關係-AutoMapperMappingProfile

/// <summary>
/// 這裡的 Profile作用是Program中services.AddAutoMapper時它會自動找到所有繼承了Profile的類然後進行配置
/// </summary>
public class AutoMapperMappingProfile:Profile
{
    public AutoMapperMappingProfile() {
        CreateMap<User, UserDto>().ReverseMap();//ReverseMap雙向對映
    }

}

注:預設是欄位同名自動對映,若欄位不同名則需要設定對映規則

4.Program.cs中注入對映服務

builder.Services.AddAutoMapper(typeof(AutoMapperMappingProfile));

5.使用

[ApiController]
[Route("[controller]")]
public class UserController(IUserService userService,IMapper mapper) : ControllerBase
{
    
    [HttpGet("GetSingleUserInfo")]
    public async Task<UserDto> GetSingleUserInfo(string account)
    {
        var info= await userService.GetSingleUserInfo(account);
        return mapper.Map<UserDto>(info);
    }
}

對映配置改進

針對以上的對映來說,每次有新的對映關係都需要到AutoMapperMappingProfile配置檔案中配置,這種方式操作起來顯得繁瑣,還可能會遺忘,為了方便操作,做了以下改進,讓每一個對映關係自動加入到配置中

1.建立特性PropertyMapperAttribute

 /// <summary>
 /// ValidOn 規定特性可被使用的範圍它是列舉器 AttributeTargets的值的組合。預設值是 AttributeTargets.All表示在所有元素都可使用。如果只允許在類或者屬性上使用可以定義:AttributeTargets.Class或者AttributeTargets.Property
 /// AllowMultiple(可選的)bool型別。如果為 true,則該特性是多用的。預設值是 false(單用的)
 /// Inherited(可選的)bool型別。如果為 true,則該特性可被派生類繼承。預設值是 false(不被繼承)
 /// </summary>
 [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
 public class PropertyMapperAttribute:Attribute
 {
     public string SourceName { get; set; }
     //public Type SourceType { get; set; }
 }

2.在需要對映的Dto中新增特性

AutoMapAttribute,AutoMapper包自帶的特性用於明確對映物件,新增至類中

PropertyMapperAttribute,用於屬性的特性,本次用來表明對映的欄位(當對映雙方欄位不相同時)

[AutoMap(typeof(User))]
public class UserDto
{
    public string UserName { get; set; }

    /// <summary>
    /// 名字
    /// </summary>
    public string Name { get; set; }

    public string Password { get; set; }

    /// <summary>
    /// 使用者是否鎖定
    /// </summary>
    [PropertyMapper(SourceName = "IsLockoutEnabled")]
    public bool IsLock { get; set; }

    public string PhoneNumber { get; set; }
}

3.對映配置檔案改進-BatchAutoMapperProfile

/// <summary>
/// 自動新增對映配置(需使用特性AutoMapAttribute的DTO)
/// </summary>
public class BatchAutoMapperProfile:Profile
{
    public BatchAutoMapperProfile()
    {
        InitMapper();
    }

    private void InitMapper()
    {//尋找應用中所有包含TestApp(應用名稱)的程式集,且找程式集中名稱中帶Dto的類
        var allTypeses = AppDomain.CurrentDomain.GetAssemblies().Where(a => a.FullName.Contains("TestApp")).SelectMany(a => a.GetTypes()).Where(a=>a.Name.Contains("Dto")).ToArray();//.Where(a => a.FullName.Contains("PlantAtlas.Interface")|| a.FullName.Contains("PlantAtlas.Server")).ToArray();

        //類對映配置
        var typeList =allTypeses.Where(t=>t.GetCustomAttributes(typeof(AutoMapAttribute)).Any()).ToList();//過濾類中帶有AutoMapAttribute特性標籤的類
        typeList.ForEach(type =>
        {
            var attribute = (type.GetCustomAttributes(typeof(AutoMapAttribute)).FirstOrDefault());
            if (attribute != null)
            {
                var a = (AutoMapAttribute)attribute;
                if (a.SourceType == null) return;
                var mapper = CreateMap(a.SourceType, type);//.ReverseMap();
                //類屬性對映配置
                var propertyList = type.GetProperties().Where(t => t.GetCustomAttributes(typeof(PropertyMapperAttribute)).Any()).ToList();//過濾出帶有PropertyMapperAttribute特性的屬性做特殊對映
                propertyList.ForEach(pro =>
                {
                    var property = pro.GetCustomAttributes<PropertyMapperAttribute>().FirstOrDefault();
                    if (property != null && !string.IsNullOrEmpty(property.SourceName))
                    {
                        mapper.ForMember(pro.Name, src => src.MapFrom(property.SourceName));
                    }
                });
                mapper.ReverseMap();//ReverseMap表示雙向對映
            }
        });
        
    }
}

4.Program.cs中注入對映服務

//自動對映
builder.Services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());

注:AddAutoMapper中傳入的參數列明是那個範圍內找所有繼承了Profile的類然後進行配置

相關文章