思想 DAO(Data Access Object)數據訪問對象,是我們在做結構化資料庫訪問的時候傳輸的對象,通過這個對象我們可以與資料庫中的表建立映射關係 DTO(Data Transfer Object)是我們在與前端進行數據交換時傳遞的對象 為什麼需要設置這這兩種對象呢? 為了數據安全 如果我 ...
思想
DAO(Data Access Object)數據訪問對象,是我們在做結構化資料庫訪問的時候傳輸的對象,通過這個對象我們可以與資料庫中的表建立映射關係
DTO(Data Transfer Object)是我們在與前端進行數據交換時傳遞的對象
為什麼需要設置這這兩種對象呢?
- 為了數據安全
如果我們直接傳遞DAO的數據,我們可能回把資料庫的底庫都扒光,比如一個用戶的數據,包括用戶的Id、用戶的賬號、密碼等,我們直接傳遞到前端,用戶的密碼有可能被抓包軟體給獲取到,之後用戶賬號就可能回被盜用,而為了杜絕這種情況,我們從後端就直接把數據給換掉 - 除了DTO,甚至我們在某些後端與資料庫進行某些數據交換的時候也會設置一個中間模型,而不是直接使用DAO,因為某些資料庫表的欄位過多,我們進行業務的時候可能並用不到難麽多的欄位,所以回進行一定的截斷
轉換思路
我們這裡既然要轉換肯定是無論是基礎類型還是引用類型的屬性都可以轉換,所以我想到了利用反射
步驟:
- 獲取來來源對象的所有公開屬性
- 創建一個字典,字典的Key值為來源對象公開屬性名稱,字典的值為來源對象的公開屬性的值
- 創建出目標類的對象,獲取到目標類的所有公開屬性
- 根據目標屬性名稱獲取到來源對象屬性的值,設置目標屬性的值
代碼實現
來源類型
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Model.Entity
{
public class User
{
public int UserId { get; set; }
// QQ郵箱
public string QQEmail { get; set; }
// 昵稱
public string Nickname { get; set; }
// 密碼
public string Password { get; set; }
// 用戶狀態 0:表示online 1:表示離線 2:表示對戰中,-1:表示禁止登錄
public int Status { get; set; }
// 是否是管理員 0:不是 1:是
public bool IsAdmin { get; set; }
// 用戶的頭像
public string AvatarUrl { get; set; }
// 獲勝的場數
public int WinNumber { get; set; }
// 失敗的場數
public int LoseNumber { get; set; }
// 和棋的場數
public int DrawNumber { get; set; }
// 排名
public int RankNumber { get; set; }
// 創建時間
public DateTime CreateTime { get; set; }
// 修改時間
public DateTime UpdateTime { get; set; }
}
}
目標類型
namespace gobangBack.DTO
{
public record UserDTO
{
public int UserId { get; set; }
// QQ郵箱
public string QQEmail { get; set; }
// 昵稱
public string Nickname { get; set; }
// 用戶狀態 0:表示online 1:表示離線 2:表示對戰中,-1:表示禁止登錄
public int Status { get; set; }
// 是否是管理員 0:不是 1:是
public bool IsAdmin { get; set; }
// 用戶的頭像
public string AvatarUrl { get; set; }
// 獲勝的場數
public int WinNumber { get; set; }
// 失敗的場數
public int LoseNumber { get; set; }
// 和棋的場數
public int DrawNumber { get; set; }
// 排名
public int RankNumber { get; set; }
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Tool
{
// 將參數Param轉換為Target類型
public static class TypeExtract
{
/// <summary>
/// 將對應的類型轉換為我們要轉換的類型
/// </summary>
/// <param name="source"></param>
/// <param name="target"></param>
public static T TransformToTargetBased<E,T>(E source,Type target,bool ignoreNull=false) where T:new()
{
// 1.獲取到來源類的所有公開的屬性
PropertyInfo[] sourceInfos = source.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
// 2.將其轉換為一個Key:屬性名,Value:屬性值的字典
Dictionary<string, object> sourceDictionary = new Dictionary<string, object>();
foreach (PropertyInfo sourceInfo in sourceInfos)
{
sourceDictionary[sourceInfo.Name] = sourceInfo.GetValue(source);
}
// 3.獲取到我們的目標類型的所有的屬性
T targetObj = new T();
PropertyInfo[] targetInfos = targetObj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
// 4.將來源對象的值轉載到目標對象上
foreach (PropertyInfo targetInfo in targetInfos) {
// 5.判斷是否忽略空值
if(ignoreNull)
{
// 6.如果是忽略空值,則值為null的話就省略
if(sourceDictionary[targetInfo.Name] != null)
{
targetInfo.SetValue(targetObj, sourceDictionary[targetInfo.Name]);
}
continue;
}
else
{
// 7.如果不選擇忽略空值,則我們就直接將對應的值加到對應的屬性上
targetInfo.SetValue(targetObj, sourceDictionary[targetInfo.Name]);
}
}
return targetObj;
}
}
}
最後我再寫一個Api測試一下
[HttpGet]
public UserDTO Get() {
User user = context.Users.Where(u => u.UserId == 1).Single();
UserDTO usreDto = TypeExtract.TransformToTargetBased<User,UserDTO>(user,new UserDTO().GetType());
return usreDto;
}
呼哈,成功,這個工具類我測試過,不僅僅是基本類型可以轉換,裡面的屬性如果是引用類型一樣可以轉換,如果大家有更好的方法,希望大家分享出來,謝謝!