- A+
所属分类:.NET技术
思想
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; }
呼哈,成功,这个工具类我测试过,不仅仅是基本类型可以转换,里面的属性如果是引用类型一样可以转换,如果大家有更好的方法,希望大家分享出来,谢谢!