第一部分:
第一个基础方法,直接手写将后台每一张表数据,依次绑定到每一个对象上去,这个过程相当机械性能优,代码重复率高
第二个方法:反射泛型方法,一个方法搞定,基本够用,不过属性不一致,名称不一致,实际需求无法很好满足,
第三个方法: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(stu);
//第三种:利用json,将对象序列化后,再反序列回来
string stustr=JsonConvert.SerializeObject(stu);
StudentView sv3 = (StudentView)JsonConvert.DeserializeObject(stustr);
//第四种:利用成熟一直在使用的,基于ORM映射框架写的automapper
//var config = new MapperConfiguration(cfg => cfg.CreateMap());
//初始化,调用一个创建方法,传进去lambda表达式,
//泛型前面是源类型,后面是生成目标类型
Mapper.Initialize(x => x.CreateMap());
StudentView sv4 = Mapper.Map(stu);
//一开始是用的适合4.6的dll,结果很诡异,提示引入命名空间,但是生成,调试又提示命名空间不存在
}
//泛型方法,自定义两个泛型即可,需要变量
private static Tout Trans(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和tin
PropertyInfo targetprop = typein.GetProperty(prop.Name);
prop.SetValue(tout,targetprop.GetValue(tin));
}
//字段也差不多
foreach (FieldInfo prop in typeout.GetFields())
{
//输入传入指定的实例对象,比如这里的tout和tin
FieldInfo 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> dic = new Dictionary>();
public static Dictionary dic = new Dictionary();
public static Tout Trans(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 memberBindingList = new List();
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> lambda = Expression.Lambda>(expressionBody, new ParameterExpression[] { parameterExpression });
Func resultfun = lambda.Compile();
dic.Add(funckey,resultfun);
}
Tout result = ((Func)dic[funckey]).Invoke(tin);
return result;
}
}
//静态构造函数不能直接调用,并且仅应由公共语言运行时 (CLR) 调用。 可以自动调用它们。
class ExpressionGenericMapper{
public static Func _Fun=null;
static ExpressionGenericMapper(){
ParameterExpression parameterExpression = Expression.Parameter(typeof(Tin));
List memberBindingList = new List();
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> lambda = Expression.Lambda>(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(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和tin
PropertyInfo targetprop = typein.GetProperty(prop.Name);
prop.SetValue(tout, targetprop.GetValue(tin));
}
//字段也差不多
foreach (FieldInfo prop in typeout.GetFields())
{
//输入传入指定的实例对象,比如这里的tout和tin
FieldInfo targetField = typein.GetField(prop.Name);
targetField.SetValue(tout, targetField.GetValue(tin));
}
return tout;
}
static void Main(string[] args)
{
//到底什么是表达式目录树
List list = new List() { 1,23,3};
var li = list.AsQueryable();
//接收的是一个表达式目录树,生成Fun委托,
li.Where(i=>i>50);
//这里Where接收的是一个Expression>类型实例,
//这个实例,调用compile一下,得到一个Func类型的委托
//怎么理解呢?对委托进行的封装,根据lambda表达式视为表达式目录树,生成一个委托的实例
//li.Where<
StudentTable student = new StudentTable()
{
ID = "123",
NAME = "张三",
QQ = "2342333323"
};
//现在封装一下简单,利用一个委托,利用委托得到目标对象
Func fun = stu => new StudentView
{
ID = stu.ID,
NAME = stu.NAME,
QQ = stu.QQ
};
//这个委托,接收一个对象,返回一个对象
//利用表达式目录树,进行拼接生成一个委托,
//表示命名的表达式,即s=>中的s参数,
ParameterExpression parameterExpression = Expression.Parameter(typeof(StudentTable));
//所有需要绑定的成员,包括属性和字段
//MemberBinding初始化新建对象的成员
List memberBindingList = new List();
//利用反射,构建每一项绑定对象,
//这里先获取需要生成对象属性,对他的属性进行赋值
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> lambda = Expression.Lambda>(expressionBody, new
ParameterExpression[] { parameterExpression });
//需要的是绑定好的一组属性字段的,待生成对象,以及传入的参数列表,生成指定的委托
Func resultfun = lambda.Compile();
//最后调用这个委托,即生成返回结果
StudentView sv= resultfun.Invoke(student);
//测试封装之后的
StudentView svnew = ExpressionMapper.Trans(student);
StudentView svnew2 = ExpressionMapper.Trans(student);
//静态构造函数不能直接调用,并且仅应由公共语言运行时 (CLR) 调用。 可以自动调用它们。
StudentView svnew3 = ExpressionGenericMapper.Trans(student);
StudentView svnew4 = ExpressionGenericMapper.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(student);
}
}
using (new TestGC("第三种:JsonConvert"))
{
for (int i = 0; i < 1000000; i++)
{
string stustr = JsonConvert.SerializeObject(student);
svtest = (StudentView)JsonConvert.DeserializeObject(stustr);
}
}
using (new TestGC("第四种:AutoMapper"))
{
Mapper.Initialize(x => x.CreateMap());
for (int i = 0; i < 1000000; i++)
{
svtest = Mapper.Map(student);
}
}
using (new TestGC("第五种:字典缓存"))
{
for (int i = 0; i < 1000000; i++)
{
svtest = ExpressionMapper.Trans(student);
}
}
using (new TestGC("第六种:泛型缓存"))
{
for (int i = 0; i < 1000000; i++)
{
svtest = ExpressionGenericMapper.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();
}
}
}