当前位置: 代码迷 >> 综合 >> c#实现数据库数据绑定到数据模型的六种方法(一)硬核编码,反射泛型,json序列化,AutoMapper,x表达式目录树
  详细解决方案

c#实现数据库数据绑定到数据模型的六种方法(一)硬核编码,反射泛型,json序列化,AutoMapper,x表达式目录树

热度:10   发布时间:2024-02-12 00:39:51.0

第一部分:

第一个基础方法,直接手写将后台每一张表数据,依次绑定到每一个对象上去,这个过程相当机械性能优,代码重复率高

第二个方法:反射泛型方法,一个方法搞定,基本够用,不过属性不一致,名称不一致,实际需求无法很好满足,

第三个方法:json,性能也不够好,毕竟序号化这个用途不是用来类型转换的,

第四个方法:Automapper功能很强悍,是封装好的,用emit实现,它是基于IL实现,调试不易,

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
//using Microsoft.VisualStudio.Profiler;
//using AutoMapper.Configuration;
using Newtonsoft.Json;
using AutoMapper;
//using AutoMapper;namespace AttributeDTO
{class Program{static void Main(string[] args){//有一个诉求,需要将后台数据库查出数据模型,转为前端需要的模型,如下://它们很相似,但没有继承关系,不能直接转换//第一种:最笨的办法,缺乏灵活性,需要很多模型怎么办?属性很多怎么办?StudentTable stu = new StudentTable() { ID="123",NAME="张三",QQ="2342333323"};new StudentView{ID = stu.ID,NAME = stu.NAME,QQ = stu.QQ};//第二种:利用反射和泛型,StudentView sv2 = Trans<StudentView, StudentTable>(stu);//第三种:利用json,将对象序列化后,再反序列回来string stustr=JsonConvert.SerializeObject(stu);StudentView sv3 = (StudentView)JsonConvert.DeserializeObject<StudentView>(stustr);//第四种:利用成熟一直在使用的,基于ORM映射框架写的automapper//var config = new MapperConfiguration(cfg => cfg.CreateMap<StudentView, StudentTable>());//初始化,调用一个创建方法,传进去lambda表达式,//泛型前面是源类型,后面是生成目标类型Mapper.Initialize(x => x.CreateMap<StudentTable,StudentView>());StudentView sv4 = Mapper.Map<StudentTable, StudentView>(stu);//一开始是用的适合4.6的dll,结果很诡异,提示引入命名空间,但是生成,调试又提示命名空间不存在}//泛型方法,自定义两个泛型即可,需要变量private static Tout Trans<Tout,Tin>(Tin tin){//获取输出对象的所有属性,依次进行赋值Type typeout = typeof(Tout);Tout tout = (Tout)Activator.CreateInstance(typeout);Type typein = tin.GetType();PropertyInfo[] props = typeout.GetProperties();typeout.GetFields();foreach (PropertyInfo prop in typeout.GetProperties()){//输入传入指定的实例对象,比如这里的tout和tinPropertyInfo targetprop = typein.GetProperty(prop.Name);prop.SetValue(tout,targetprop.GetValue(tin));}//字段也差不多foreach (FieldInfo prop in typeout.GetFields()){//输入传入指定的实例对象,比如这里的tout和tinFieldInfo targetField = typein.GetField(prop.Name);targetField.SetValue(tout, targetField.GetValue(tin));}return tout;}}class StudentTable {public String ID { set; get; }public String NAME { set; get; }public String QQ { set; get; }}class StudentView{public String ID{set;get;}public String NAME { set; get; }public String QQ { set; get; }}
}

第二部分:综合特性应用和表达式目录树的应用,

表达式目录树,可以用来动态生成lambda委托,从而执行委托将数据绑定上去,

关于表达式目录树相关知识仍然不算熟悉,写在这里方便翻阅,主要参考朝夕教育腾讯课堂eleven老师所写

官方对表达式目录树相关举例子:

https://docs.microsoft.com/zh-cn/dotnet/csharp/expression-trees-building

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;namespace ExpressionAttributeDemo
{class ExpressionMapper{//public static Dictionary<string, Func<Tin, Tout>> dic = new Dictionary<string, Func<Tin, Tout>>();public static Dictionary<string, object> dic = new Dictionary<string, object>();public static Tout Trans<Tin,Tout>(Tin tin) { string funckey = string.Format("funckey_{0}_{1}",typeof(Tin).FullName,typeof(Tout).FullName);if (!dic.ContainsKey(funckey)){ParameterExpression parameterExpression = Expression.Parameter(typeof(Tin));List<MemberBinding> memberBindingList = new List<MemberBinding>();foreach (PropertyInfo item in typeof(Tout).GetProperties()){MemberExpression property = Expression.Property(parameterExpression, typeof(Tin).GetProperty(item.Name));MemberBinding memberBinding = Expression.Bind(item, property);memberBindingList.Add(memberBinding);}foreach (FieldInfo item in typeof(Tout).GetFields()){MemberExpression Field = Expression.Field(parameterExpression, typeof(Tin).GetField(item.Name));MemberBinding memberBinding = Expression.Bind(item, Field);memberBindingList.Add(memberBinding);}Expression expressionBody = Expression.MemberInit(Expression.New(typeof(Tout)), memberBindingList.ToArray());Expression<Func<Tin, Tout>> lambda = Expression.Lambda<Func<Tin, Tout>>(expressionBody, new ParameterExpression[] { parameterExpression });Func<Tin, Tout> resultfun = lambda.Compile();dic.Add(funckey,resultfun);}Tout result = ((Func<Tin, Tout>)dic[funckey]).Invoke(tin);return result;}}//静态构造函数不能直接调用,并且仅应由公共语言运行时 (CLR) 调用。 可以自动调用它们。class ExpressionGenericMapper<Tin, Tout>{public static Func<Tin,Tout> _Fun=null;static ExpressionGenericMapper(){ParameterExpression parameterExpression = Expression.Parameter(typeof(Tin));List<MemberBinding> memberBindingList = new List<MemberBinding>();foreach (PropertyInfo item in typeof(Tout).GetProperties()){MemberExpression property = Expression.Property(parameterExpression, typeof(Tin).GetProperty(item.Name));MemberBinding memberBinding = Expression.Bind(item, property);memberBindingList.Add(memberBinding);}foreach (FieldInfo item in typeof(Tout).GetFields()){MemberExpression Field = Expression.Field(parameterExpression, typeof(Tin).GetField(item.Name));MemberBinding memberBinding = Expression.Bind(item, Field);memberBindingList.Add(memberBinding);}Expression expressionBody = Expression.MemberInit(Expression.New(typeof(Tout)), memberBindingList.ToArray());Expression<Func<Tin, Tout>> lambda = Expression.Lambda<Func<Tin, Tout>>(expressionBody, new ParameterExpression[] { parameterExpression });_Fun = lambda.Compile();}public static Tout Trans(Tin tin){return _Fun(tin);}}
}
using AutoMapper;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;namespace ExpressionAttributeDemo
{class Program{//泛型方法,自定义两个泛型即可,需要变量private static Tout Trans<Tout, Tin>(Tin tin){//获取输出对象的所有属性,依次进行赋值Type typeout = typeof(Tout);Tout tout = (Tout)Activator.CreateInstance(typeout);Type typein = tin.GetType();PropertyInfo[] props = typeout.GetProperties();typeout.GetFields();foreach (PropertyInfo prop in typeout.GetProperties()){//输入传入指定的实例对象,比如这里的tout和tinPropertyInfo targetprop = typein.GetProperty(prop.Name);prop.SetValue(tout, targetprop.GetValue(tin));}//字段也差不多foreach (FieldInfo prop in typeout.GetFields()){//输入传入指定的实例对象,比如这里的tout和tinFieldInfo targetField = typein.GetField(prop.Name);targetField.SetValue(tout, targetField.GetValue(tin));}return tout;}static void Main(string[] args){//到底什么是表达式目录树List<int> list = new List<int>() { 1,23,3};var li = list.AsQueryable();//接收的是一个表达式目录树,生成Fun<int,bool>委托,li.Where<int>(i=>i>50);//这里Where接收的是一个Expression<Func<int,bool>>类型实例,//这个实例,调用compile一下,得到一个Func<int,bool>类型的委托//怎么理解呢?对委托进行的封装,根据lambda表达式视为表达式目录树,生成一个委托的实例//li.Where<StudentTable student = new StudentTable(){ID = "123",NAME = "张三",QQ = "2342333323"};//现在封装一下简单,利用一个委托,利用委托得到目标对象Func<StudentTable, StudentView> fun = stu =>  new StudentView{ID = stu.ID,NAME = stu.NAME,QQ = stu.QQ};//这个委托,接收一个对象,返回一个对象//利用表达式目录树,进行拼接生成一个委托,//表示命名的表达式,即s=>中的s参数,ParameterExpression parameterExpression = Expression.Parameter(typeof(StudentTable));//所有需要绑定的成员,包括属性和字段//MemberBinding初始化新建对象的成员List<MemberBinding> memberBindingList = new List<MemberBinding>();//利用反射,构建每一项绑定对象,//这里先获取需要生成对象属性,对他的属性进行赋值foreach (PropertyInfo item in typeof(StudentView).GetProperties()){//给传入参数条件对象表达式,和对应的Property对象创建一个属性,MemberExpression property = Expression.Property(parameterExpression, typeof(StudentTable).GetProperty(item.Name));//有点像是根据传入条件对象,拆解出来一个属性,//生成一项绑定项,即需要生成对象属性,和传入对象的属性,之间的绑定关系MemberBinding memberBinding= Expression.Bind(item, property);//前面是需要绑定的项,后面绑定具体表达式值,memberBindingList.Add(memberBinding);}//这里先获取需要生成对象属性,对他的属性进行赋值foreach (FieldInfo item in typeof(StudentView).GetFields()){//给传入参数条件对象表达式,和对应的Property对象创建一个属性,MemberExpression Field = Expression.Field(parameterExpression, typeof(StudentTable).GetField(item.Name));//有点像是根据传入条件对象,拆解出来一个属性,//生成一项绑定项,即需要生成对象属性,和传入对象的属性,之间的绑定关系MemberBinding memberBinding = Expression.Bind(item, Field);//前面是需要绑定的项,后面绑定具体表达式值,memberBindingList.Add(memberBinding);}//总体而言,将需要生成对象属性字段等进行绑定,即进行赋值,值是根据传入对象,和对象的属性字段,进行的//完成后将一系列绑定信息,放在绑定数组之中//接下来一步是:MemberInit需要一个新的new expression和绑定数组//一个对象类型的表达式对象Expression expressionBody= Expression.MemberInit(Expression.New(typeof(StudentView)), memberBindingList.ToArray());//重头戏来了,生成目标lambda表达式Expression<Func<StudentTable, StudentView>> lambda = Expression.Lambda<Func<StudentTable, StudentView>>(expressionBody, newParameterExpression[] { parameterExpression });//需要的是绑定好的一组属性字段的,待生成对象,以及传入的参数列表,生成指定的委托Func<StudentTable, StudentView> resultfun = lambda.Compile();//最后调用这个委托,即生成返回结果StudentView sv= resultfun.Invoke(student);//测试封装之后的StudentView svnew = ExpressionMapper.Trans<StudentTable, StudentView>(student);StudentView svnew2 = ExpressionMapper.Trans<StudentTable, StudentView>(student);//静态构造函数不能直接调用,并且仅应由公共语言运行时 (CLR) 调用。 可以自动调用它们。StudentView svnew3 = ExpressionGenericMapper<StudentTable, StudentView>.Trans(student);StudentView svnew4 = ExpressionGenericMapper<StudentTable, StudentView>.Trans(student);StudentView svtest = null;using(new TestGC("第一种:直接转换")){for (int i = 0; i < 1000000; i++){                   svtest=new StudentView{ID = student.ID,NAME = student.NAME,QQ = student.QQ};}}using (new TestGC("第二种:泛型反射")){for (int i = 0; i < 1000000; i++){svtest = Trans<StudentView, StudentTable>(student);}}using (new TestGC("第三种:JsonConvert")){for (int i = 0; i < 1000000; i++){string stustr = JsonConvert.SerializeObject(student);svtest = (StudentView)JsonConvert.DeserializeObject<StudentView>(stustr);}}using (new TestGC("第四种:AutoMapper")){Mapper.Initialize(x => x.CreateMap<StudentTable, StudentView>());for (int i = 0; i < 1000000; i++){                  svtest = Mapper.Map<StudentTable, StudentView>(student);}}using (new TestGC("第五种:字典缓存")){for (int i = 0; i < 1000000; i++){svtest = ExpressionMapper.Trans<StudentTable, StudentView>(student);}}using (new TestGC("第六种:泛型缓存")){for (int i = 0; i < 1000000; i++){svtest = ExpressionGenericMapper<StudentTable, StudentView>.Trans(student);}}}}class StudentTable{public String ID { set; get; }public String NAME { set; get; }public String QQ { set; get; }}class StudentView{public String ID { set; get; }public String NAME { set; get; }public String QQ { set; get; }}internal sealed class TestGC : IDisposable {private Stopwatch sw=new Stopwatch();private string text;private int count;//构造开始便进行一次重置public TestGC(string t) {PrepareForOperation();text = t;//同时统计一次垃圾回收次数:count = GC.CollectionCount(0);//时间重新开始计时sw.Start();}//using内容结束时,释放资源,统计垃圾回收次数public void Dispose() {Console.WriteLine("{0},执行时间:{1}ms,垃圾回收次数:{2}",text,sw.ElapsedMilliseconds,GC.CollectionCount(0)-count);}//void IDisposable.Dispose()//{//    throw new NotImplementedException();//}//}///相当于进行垃圾回收重置操作,即先垃圾回收上一次遗留,并确保完成///并重新开始垃圾回收private static void PrepareForOperation(){GC.Collect();GC.WaitForPendingFinalizers();GC.Collect();}}}

 

  相关解决方案