zoukankan      html  css  js  c++  java
  • 一步步实现自己的ORM(三)

    章节列表:

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

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

    通过前面两篇文章,我们大致了解了ORM的基本原理,是通过Attribute+反射获取表的基本信息,再用表名和字段名拼接成SQL语句。而前面我们只是完成了CRUD部分的CUD,还没完成读取(Retrieve)操作,今天就来完成这个R操作。我们先看下平时写实体类转换代码是怎么写的:

            static List<User> ToObject1()
            {
                SqlDataReader reader = null;
                List<User> list = new List<User>();
                while (reader.Read())
                {
                    User user = new User();
                    if (reader["UserId"] != null)
                        user.UserId = (int)reader["UserId"];
    
                    if (reader["Email"] != null)
                        user.Email = (string)reader["Email"];
    
                    if (reader["CreatedTime"] != null)
                        user.CreatedTime = (DateTime)reader["CreatedTime"];
    
                    list.Add(user);
                }
                return list;
            }

    经常写DataReader对象转换成实体类的童鞋们,WHILE循环里的内容是根据实体类的不同代码而有所不同,循环外的代码则是固定不变的,现在我们要把变化的代码抽离出来,我们还是请出明星--反射。

    在前面章节我们都是用反射读取实体类里的,比如typeof(User).GetProperty("UserId").GetValue(obj,null),有GetValue肯定也有SetValue,我们看下SetValue的定义:

    public virtual void SetValue(
        Object obj,
        Object value,
        Object[] index
    )

    obj:将设置其属性值的对象。

    value:新的属性值。

    index : 索引化属性的可选索引值。 对于非索引化属性,该值应为 null。 

    示例代码:

                User user = new User();
                typeof(User).GetProperty("UserId").SetValue(user, 10, null);
                Console.WriteLine(user.UserId);

    输出结果我就不贴图了,是“10”。

    HOHO,我们开始构建代码,步骤如下:

      1. new 实体类对象

      2. 反射类的属性,然后用SetValue给实体类属性赋值

    具体代码如下:

                string sql = "select * from tb_users";
                SqlConnection conn = new SqlConnection(EntityHelper.connectionString);
                conn.Open();
    
               var cmd =  conn.CreateCommand();
               cmd.CommandText = sql;
    
                SqlDataReader reader = cmd.ExecuteReader();
                List<User> list = new List<User>();
                var type = typeof(User);
                var properties = type.GetProperties();
                while (reader.Read())
                {
                    User user = new User();
                    for (int i = 0; i < properties.Length; i++)
                    {
                        var pi = properties[i];
                        if (reader[pi.Name] != null) //等同于 if (reader["UserId"] != null)这样的语句
                            pi.SetValue(user, reader[pi.Name], null); //等同于 user.UserId = (int)reader["UserId"];
                    }
    
                    list.Add(user);
                }
    
                foreach (var item in list)
                {
                    Console.WriteLine("UserId:{0},Email:{1},CreatedTime:{2}", item.UserId, item.Email, item.CreatedTime);
                }
    View Code

    运行结果:

    下面一个问题来了,我们看这一行代码

    User user = new User();

    难道我们每次都要这么写这样的代码吗,能不能做一个通用的吗?这需要我们用反射实例化一个类,我们请Activator.CreateInstance帮忙创建实例,代码如下:

                    var type = typeof(User);
                    var user = Activator.CreateInstance(type);

    创建实例和给属性赋值都动态完成,我稍稍修改下代码,只要传递SQL语句就可以返回集合。

            static List<T> GetEntityList<T>(string sql)
            {
                SqlConnection conn = new SqlConnection(EntityHelper.connectionString);
                conn.Open();
    
                var cmd = conn.CreateCommand();
                cmd.CommandText = sql;
    
                SqlDataReader reader = cmd.ExecuteReader();
                List<T> list = new List<T>();
                var type = typeof(T);
                var properties = type.GetProperties();
                while (reader.Read())
                {
                    var user = Activator.CreateInstance(type);
                    for (int i = 0; i < properties.Length; i++)
                    {
                        var pi = properties[i];
                        if (reader[pi.Name] != null) //等同于 if (reader["UserId"] != null)这样的语句
                            pi.SetValue(user, reader[pi.Name], null); //等同于 user.UserId = (int)reader["UserId"];
                    }
    
                    list.Add((T)user);
                }
                return list;
            }
    View Code

    最后我们在增加如下方法:

    Get(object[] values) 根据主键获取实体类

    GetList<T>(string where,string orderBy) 按条件查询实体类

            public static T Get<T>(object[] values)
            {
                var type = typeof(T);
                Dictionary<string, object> parameters = new Dictionary<string, object>();
                var properties = type.GetProperties();
    
                string tableName = string.Empty;
                TableAttribute[] tableAttrs = (TableAttribute[])type.GetCustomAttributes(typeof(TableAttribute), true);
                if (tableAttrs.Length > 0)
                {
                    tableName = tableAttrs[0].Name;
                }
                else
                {
                    tableName = type.Name;
                }
    
                /*将所有的列放到集合里*/
                List<IdAttribute> columns = new List<IdAttribute>();
                for (int i = 0; i < properties.Length; i++)
                {
                    var pi = properties[i];
                    var attrs = (IdAttribute[])pi.GetCustomAttributes(typeof(IdAttribute), true);
                    if (attrs.Length > 0)
                    {
                        columns.Add(attrs[0]);
                    }
                }
                if (columns.Count != values.Length)
                    throw new ArgumentException("参数个数和主键数不一致");
    
                StringBuilder sql = new StringBuilder();
                sql.Append("SELECT * FROM [").Append(tableName).Append("] ").Append(" WHERE ");
    
                for (int i = 0; i < columns.Count; i++)
                {
                    if (i > 0) //考虑到有多个主键
                        sql.Append(" AND ");
    
                    sql.Append(columns[i].Name).Append("=").Append("@p").Append(i);
    
                    /*参数*/
                    parameters.Add("@p" + i, values[i]);
                }
                Console.WriteLine(sql);
                return GetEntityList<T>(sql.ToString(), parameters).FirstOrDefault();
            }
            public static List<T> GetList<T>(string where,string orderBy)
            {
                var type = typeof(T);
                Dictionary<string, object> parameters = new Dictionary<string, object>();
                var properties = type.GetProperties();
    
                string tableName = string.Empty;
                TableAttribute[] tableAttrs = (TableAttribute[])type.GetCustomAttributes(typeof(TableAttribute), true);
                if (tableAttrs.Length > 0)
                {
                    tableName = tableAttrs[0].Name;
                }
                else
                {
                    tableName = type.Name;
                }
    
    
                StringBuilder sql = new StringBuilder();
                sql.Append("SELECT * FROM [").Append(tableName).Append("] ");
                if (string.IsNullOrEmpty(where))
                    sql.Append(" WHERE ").Append(where);
    
                if (string.IsNullOrEmpty(orderBy))
                    sql.Append(" Order By ").Append(orderBy);
    
    
                Console.WriteLine(sql);
                return GetEntityList<T>(sql.ToString(), parameters);
            }
    
            public static List<T> GetEntityList<T>(string sql, Dictionary<string, object> parameters)
            {
                SqlConnection conn = new SqlConnection(EntityHelper.connectionString);
                conn.Open();
    
                var cmd = conn.CreateCommand();
                cmd.CommandText = sql;
                if (parameters != null && parameters.Count > 0)
                {
                    foreach (var item in parameters)
                    {
                        var pa = cmd.CreateParameter();
                        pa.ParameterName = item.Key;
                        pa.Value = item.Value ?? DBNull.Value;
                        cmd.Parameters.Add(pa);
                    }
                }
    
                SqlDataReader reader = cmd.ExecuteReader();
                List<T> list = new List<T>();
                var type = typeof(T);
                var properties = type.GetProperties();
                while (reader.Read())
                {
                    var user = Activator.CreateInstance(type);
                    for (int i = 0; i < properties.Length; i++)
                    {
                        var pi = properties[i];
                        if (reader[pi.Name] != null) //等同于 if (reader["UserId"] != null)这样的语句
                            pi.SetValue(user, reader[pi.Name], null); //等同于 user.UserId = (int)reader["UserId"];
                    }
    
                    list.Add((T)user);
                }
                return list;
            }
    View Code

     下载代码:http://files.cnblogs.com/files/sobaby/ORM03.zip

    EntityHelper里重复代码很多,而且采用反射效率很低,下一章就开始优化代码。

  • 相关阅读:
    android之AlertDialog 点击其它区域自己主动消失
    leetCode191/201/202/136 -Number of 1 Bits/Bitwise AND of Numbers Range/Happy Number/Single Number
    CEF 框架使用集锦
    Qt WebEngine Debugging and Profiling
    Qt内置浏览器引擎WebEngine调试和分析方法
    QWebEngine踩坑记录
    带外(out of band)数据
    碰撞检测算法:点和矩形碰撞、点和圆形碰撞、矩形碰撞、圆形碰撞
    windows 7 安装visual studio 2019 闪退问题解决
    最小二乘法
  • 原文地址:https://www.cnblogs.com/sobaby/p/4350205.html
Copyright © 2011-2022 走看看