自研ORM(二)单表查询升级到支持多种表查询

本分类以最简单的ADO.NET为基础,通过一步步升级完善,形成自研的ORM框架。针对每小章所运用的技术,会在文章开头有大概描述。

 

平台环境: VS2022   基于.NET 6

 本章目标:由单表查询升级到支持多个不同表的查询,动态生成SQL语句,动态赋值。

本章使用到的技术点:   ADO.NET 、泛型、反射、Linq


笔者首先写了一个根据ID查询固定表的方法

        /// <summary>
        /// 根据主键查询
        /// 目前此方法只针对一个实体(表),无法做到通用
        /// </summary>
        /// <param name="id"></param>
        public Company GetCompany(int id) 
        {
            Company company = new Company();
          using   SqlConnection connection = new SqlConnection(connectionString);
            connection.Open();
            string sql = @"SELECT [Id]
                                  ,[Name]
                                  ,[CreatorId]
                                  ,[LastModifierId]
                                  ,[CreateTime]
                                  ,[LastModifyTime]
                              FROM[AdvancedCustomerDB].[dbo].[Company] where id=" + id;
            SqlCommand cmd = connection.CreateCommand();
            cmd.CommandText = sql;
            SqlDataReader dr = cmd.ExecuteReader();
            if (dr.Read())
            {
                company.Id= Convert.ToInt32( dr["id"]);
                company.Name = Convert.ToString(dr["Name"]);
                company.CreatorId = Convert.ToInt32(dr["CreatorId"]);

                company.LastModifierId = Convert.ToInt32(dr["LastModifierId"]);
                company.LastModifyTime = Convert.ToDateTime(dr["LastModifyTime"]);
                company.CreateTime = Convert.ToDateTime(dr["CreateTime"]);


            }

            return company;
        }

 

在完成上述代码后,带着如下几个疑问进行思考

1.如和做到一个方法满足多种诉求?

     这里 我们首先想到的肯定是泛型

2.如果做到一个方法满足多种诉求后,我们调用方法怎么完成不同实体(意味着不同的表)的数据绑定

   需要用到反射,反射可以获取对象、属性以及给属性赋值

3.如何实现SQL语句的编写(不同的表)

实体类和数据库的表是完全一致,获取实体类的属性就相当于获取表的字段

获取的对象就是数据库的表名称
动态生成SQL语句

 

        /// <summary>
        /// 升级版(1)
        /// 支持不同表的查询
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="id"></param>
        /// <returns></returns>
        public T FindByID<T>(int id) where  T:new ()//泛型约束,必须是无参构造方法
        {
            Type type = typeof(T);
            object? result= Activator.CreateInstance(type);//反射创建实例,调用的是无参构造函数,这里需要type类型

            using SqlConnection connection = new SqlConnection(connectionString);
            connection.Open();
            //思考: 既然是针对多个不同的表,那写死的SQL语句肯定无法满足需求
            #region 固定SQL语句(只支持单表)
            //string sql = @"SELECT [Id]
            //                      ,[Name]
            //                      ,[CreatorId]
            //                      ,[LastModifierId]
            //                      ,[CreateTime]
            //                      ,[LastModifyTime]
            //                  FROM[AdvancedCustomerDB].[dbo].[Company] where id=" + id;
            #endregion

            //SQL语句也应该依赖T类,动态生成

            //实体类和数据库的表是完全一致,获取实体类的属性就相当于获取表的字段
            //获取的对象就是数据库的表名称
            //动态生成SQL语句
            string sql = $"select {string.Join(",", type.GetProperties().Select(c => c.Name).ToList())} from {type.Name} where id=" + id;


            SqlCommand cmd = connection.CreateCommand();
            cmd.CommandText = sql;
            SqlDataReader dr = cmd.ExecuteReader();
            if (dr.Read())
            {
                //思考:这里其实就是属性值的绑定,如何做到通用呢?
                //需要利用反射来实现
                //反射如何给对象的属性赋值?1.获取对象 2获取属性 3给属性赋值

                #region 原有针对单个表完成数据绑定
                {
                    //company.Id = Convert.ToInt32(dr["id"]);
                    //company.Name = Convert.ToString(dr["Name"]);
                    //company.CreatorId = Convert.ToInt32(dr["CreatorId"]);

                    //company.LastModifierId = Convert.ToInt32(dr["LastModifierId"]);
                    //company.LastModifyTime = Convert.ToDateTime(dr["LastModifyTime"]);
                    //company.CreateTime = Convert.ToDateTime(dr["CreateTime"]);
                }
                #endregion
                
                
                {
                    //循环遍历属性,逐个给属性赋值
                    foreach (var item in type.GetProperties())
                    {
                        //思考:不同的表,属性肯定存在差异,如果每个属性都这么判断赋值,那肯定无法做到通用
                        //观察如下代码,可以发现,每次循环必然只进入一个判断,且都是取出dr[item.name]

                        //if (item.Name.Equals("id"))
                        //{
                        //    item.SetValue(result, dr[item.Name]);
                        //}
                        //else if (item.Name.Equals("Name"))
                        //{
                        //    item.SetValue(result, dr[item.Name]);

                        //}

                        //........................
                        //那我们可以不用判断,直接取item.name
                        //如果数据库表中的某一个字段为null,reader[prop.Name]获取到的名称为DBNull----在C#程序中的代表的是null; 但是在C#程序中,null 不能 直接使用 DBnull来替换 

                        item.SetValue(result, dr[item.Name] is DBNull ? null: dr[item.Name]);
                    }
                }

            }

            return (T)result;
        }

 

posted @ 2023-01-10 20:50  唐什么来着  阅读(36)  评论(0编辑  收藏  举报