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

第一部分:

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

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

第三个方法: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();
            }
       
    }

}

 

你可能感兴趣的:(c#)