一步步实现自己的ORM(五)

上一张优化了ORM的INSERT、UPDATE、DELETE,但将数据库里的值填充到实体类这块还没优化。另外有博友在网上咨询说你这个都是查询所有字段的,而他的需求是按需查询字段,不是一次性取出来所有字段的,在这里我请这位朋友耐心等待,这个会在后面章节提到的。这次我们先优化datareader->entity,将数据库里的值Mapping到实体类里,我们常用的有两种办法第一种是采用EMIT方式,第二种是采用Expression tree表达式,这两种方案在性能上差异不大,但写法上第二种较第一种比较容易,我在这里采用Expression tree表达式来完成实体类转换操作。

让我们先来看下Expression 表达式如何将DataReader转换成Object:

            IDataReader reader = null;
            Expression<Func<IDataReader, User>> expr = (r) => new User()
            {
                UserId = r.GetInt32(0),
                CreatedTime = r.GetDateTime(1),
                Email = r.GetString(2),
            };

            var func = expr.Compile();
            func(reader);

在new User的时候采用对象初始化方式给属性赋值,而在实际项目中我们会遇到更复杂的,比如还要判断是否为DbNull,要不然转换会出错,在这里我定义一个GetValue<T>(int i)方法,专门做取值操作。

    class DbFieldReader
    {
        private IDataReader reader;
        public DbFieldReader(IDataReader reader)
        {
            this.reader = reader;
        }

        public T GetValue<T>(int index)
        {
            object value = reader.GetValue(index);

            if (value == DBNull.Value)
                return default(T);

            return (T)reader.GetValue(index);
        }
    }

现在将代码修改下,

            IDataReader reader = null;
            DbFieldReader fr = new DbFieldReader(reader);
            Expression<Func<DbFieldReader, User>> expr = (r) => new User()
            {
                UserId = r.GetValue<int>(0),
                CreatedTime = r.GetValue<DateTime>(1),
                Email = r.GetValue<string>(2),
            };

            var func = expr.Compile();
            func(fr);

是不是简洁了很多?把判断代码转移到DbFieldReader类中处理,现在我们要将这个方法做成通用实体类转换,先分解下Expression表达式(如吐有错误,恳请大神指教)。

第一步定义参数,类型为DbFieldReader,参数名:r

ParameterExpression parExpr = Expression.Parameter(typeof(DbFieldReader), "r");

第二步,调用User类的构造函数,代码如下:

var newExpr = Expression.New(type.GetConstructors().First());

合并这2个表达式和运行结果:

Expression<Func<DbFieldReader, User>> expr = (Expression<Func<DbFieldReader, User>>)Expression.Lambda(newExpr, parExpr);

基本的雏形出来了,现在要做的就是给属性赋值,怎么来呢?先来个简单的,就是给固定值。

var userid = Expression.Bind(type.GetProperty("UserId"), Expression.Constant(1, typeof(int)));

这就相当于UserId = 1的赋值操作,继续合并表达式:

          var type = typeof(User);
            ParameterExpression parExpr = Expression.Parameter(typeof(DbFieldReader), "r");

            var newExpr = Expression.New(type.GetConstructors().First());

            var userid = Expression.Bind(type.GetProperty("UserId"), Expression.Constant(1, typeof(int)));
            var CreatedTime = Expression.Bind(type.GetProperty("CreatedTime"), Expression.Constant(DateTime.Now, typeof(DateTime)));

            var init = Expression.MemberInit(newExpr, userid, CreatedTime);
            Expression<Func<DbFieldReader, User>> expr = (Expression<Func<DbFieldReader, User>>)Expression.Lambda(init, parExpr);

常量赋值没问题后,下面就要来调用方法了。

            var method = typeof(DbFieldReader).GetMethods().Where(c => c.Name == "GetValue" && c.IsGenericMethod).First();
            var callExpr = Expression.Call(parExpr, method.MakeGenericMethod(typeof(int)), Expression.Constant(0));       var userid = Expression.Bind(type.GetProperty("UserId"), callExpr);

这句话相当于 r.GetValue<int>(0);

先获取GetValue方法,然后调用MakeGenericMethod生成泛型方法,Expression.Constant(0)就是参数值。

完整版代码:

            IDataReader reader = null;
            DbFieldReader fr = new DbFieldReader(reader);

            var entityMapping = AttributeMapping.Get<User>();
            ParameterExpression parExpr = Expression.Parameter(typeof(DbFieldReader), "r");

            var newExpr = Expression.New(entityMapping.EntityType.GetConstructors().First());

            var method = typeof(DbFieldReader).GetMethods().Where(c => c.Name == "GetValue" && c.IsGenericMethod).First();

            List<MemberBinding> memberBindings = new List<MemberBinding>();
            int index = 0;
            foreach (var item in entityMapping.Members)
            {
                var callExpr = Expression.Call(parExpr, method.MakeGenericMethod(item.Member.PropertyType), Expression.Constant(index));
                var memberAssignment = Expression.Bind(item.Member, callExpr);
                memberBindings.Add(memberAssignment);
                index++;
            }

            var init = Expression.MemberInit(newExpr, memberBindings);
            Expression<Func<DbFieldReader, User>> expr = (Expression<Func<DbFieldReader, User>>)Expression.Lambda(init, parExpr);

当然我们还需要修改SQL语句,不能用SELECT * 语法,而是要用SELECT userid,email...from这样的语法

最终的代码代码下载:

http://files.cnblogs.com/files/sobaby/ORM05.zip

时间: 2024-10-20 11:45:08

一步步实现自己的ORM(五)的相关文章

一步步实现自己的ORM(二)

在第一篇<一步步实现自己的ORM(一)>里,我们用反射获取类名.属性和值,我们用这些信息开发了简单的INSERT方法,在上一篇文章里我们提到主键为什么没有设置成自增长类型,单单从属性里我们无法识别哪个是主键,今天我们用Attribute来标识列,关于Attribute,引用MSDN里描述      MADN的定义为:公共语言运行时允许添加类似关键字的描述声明,叫做attributes, 它对程序中的元素进行标注,如类型.字段.方法和属性等.Attributes和Microsoft .NET F

一步步实现自己的ORM(四)

通过前3章文章,大致对ORM有一定的了解,但也存在效率低下(大量用了反射)和重复代码,今天我们要对ORM进行优化. 具体流程如下: 我们优化的第一个就是减少反射调用,我的思路是定义一个Mapping,把表名.字段名信息缓存起来,EntityMapping 表示实体类信息对应数据库中的table,MemberMapping表示实体类的属性对应数据库中的Column. EntityMapping代码: class EntityMapping { /// <summary> /// 对应实体类类型

Django框架详解

一.WSGI剖析 二.Django-url路由系统 三.Django-views视图与逻辑处理 四.Django-Models与ORM 五.Django-Template模板

day 68 django 之api操作

前情回顾一下,: django项目 安装: 创建项目 配置(setting,static,csrf) 创建app,python manage.py startapp app1 三部分 urls.py路由配置 1,普通正则 2分组正则 传位置参数:url(r'^blog/(\d+)/',views.blog) 3分组命名(得到的是关键字参数) url(r'^blog/(?P<year>(\d+)/',views.blog'))    视图函数要传参blog(request,year) 4用nam

【SSH进阶之路】一步步重构MVC实现Struts框架——彻底去掉逻辑判断(五)

目录: [SSH进阶之路]Struts基本原理 + 实现简单登录(二) [SSH进阶之路]一步步重构MVC实现Struts框架--从一个简单MVC开始(三) [SSH进阶之路]一步步重构MVC实现Struts框架--封装业务逻辑和跳转路径(四) [SSH进阶之路]一步步重构MVC实现Struts框架--彻底去掉逻辑判断(五) [SSH进阶之路]一步步重构MVC实现Struts框架--完善转向页面,大功告成(六) Struts的第二篇博客[SSH进阶之路]Struts基本原理 + 实现简单登录(二

Android 学习笔记之AndBase框架学习(五) 数据库ORM..注解,数据库对象映射...

PS:好久没写博客了... 学习内容: 1.DAO介绍,通用DAO的简单调度过程.. 2.数据库映射关系... 3.使用泛型+反射+注解封装通用DAO.. 4.使用AndBase框架实现对DAO的调用实现数据库基本操作.. 1.DAO..   DAO..这个说法大家都不陌生..数据库操作访问对象的抽象接口层..在DAO内部封装好通用的方法..然后再使用一个具体类来进行实现..在我们想要调用这些方法对数据库进行相关操作时..只需要传递数据实体对象,就可以通过实体对象的传递从而实现对数据库进行操作.

打造android ORM框架opendroid(五)——数据更新的实现

在上一篇博客<打造android ORM框架opendroid(四)--优雅的删除数据>中,我们介绍了opendroid是如何优雅的从数据库中删除数据的,也可以看到opendroid的设计是如此的简单,其实opendroid只是我作为兴趣或者说是抱着试试的态度写的,当然它肯定存在诸多不足,但是这并不影响我们去了解一个orm框架的流程. 废话不说了,下面进入主题,今天我选择去了解的是opendroid的update流程,其实,对于已经了解了delete操作的朋友们来说,今天的update流程肯定

Django 【第五篇】ORM单表增删改查

一.添加表记录 对于单表有两种方式 # 添加数据的两种方式 # 方式一:实例化对象就是一条表记录 Frank_obj = models.Student(name ="海东",course="python",birth="2000-9-9",fenshu=80) Frank_obj.save() # 方式二: models.Student.objects.create(name ="海燕",course="python

Android 开源框架 ( 五 ) xUtils --- Android 里的 ORM IOC聚合型框架

xUtils同Afinal一样属于聚合型框架, 大而全,但是越容易牵一发而动全身.所以不推荐使用,只做了解即可.应付手头临时项目. Android 开源框架 ( 四 ) Afinal --- Android 里的 ORM IOC聚合型框架 一.Xutils 介绍 Xutils是基于afinal开发的,但是比afinal稳定性提高了不少.xUtils 最初源于Afinal框架,进行了大量重构,使得xUtils支持大文件上传,更全面的http请求协议支持(10种谓词),拥有更加灵活的ORM,更多的事