• 一步步实现自己的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

  • 相关阅读:
    C# 与 Java Rsa加密与解密互通
    PHP 读取Postgresql中的数组
    ArcGis Javascript API (V3.6)加载天地图
    Entity Framework 6.0 对枚举的支持/实体添加后会有主键反回
    ubuntu 中 ssh连接用UTF8
    Entity Framework PostgresQL CodeFirst
    Golang 字符编码
    CentOS 安装 mono
    C和C++中的不定参数
    WisDom.Net 框架设计(一) 总体框架
  • 原文地址:https://www.cnblogs.com/sobaby/p/4380654.html
Copyright © 2020-2023  润新知