反射 泛型 SQL, 求助

阿布说不 2014-06-04 12:17:55
由于感觉,用string拼接SQL,无论写起来,还是修改,都很麻烦
于是,我把SQL语句都保存到XML中,为内嵌文件!


这样写起来就简单多了!
访问数据库则是这样的!


查询的数据集则是SQLDataReader反射生成实体或者list<T>



虽然这样,服务端,写起来简单很多,但是里面大量用到了反射,后来我才知道反射非常影响性能!
现在,我项目服务端全部是这样写的!我该怎么办?全部重写,还是优化它?怎么优化?如果重写?有什么好的方法?
求助
...全文
441 35 打赏 收藏 转发到动态 举报
写回复
用AI写文章
35 条回复
切换为时间正序
请发表友善的回复…
发表回复
tcmakebest 2014-06-17
  • 打赏
  • 举报
回复
楼主还是保留现状吧,反射速度慢那是要与访问数量结合起来看的,到真的慢到受不了了再改吧,也许等不到那时候代码就已经需要重构了.
老张一笑 2014-06-17
  • 打赏
  • 举报
回复
#32 DataReader ,DataSet 做比较和反射比较没意义 反编译看过的话就会明白,DataAdaper 内部调用还是DataReader . 实在需要计较反射的效率,就是把反射的代码改成实际代码. 我的orm框架是这么实现的,本来自己的代码生成器值生成Model就可以了,考虑或许会有效率问题, 每个表又生成了把DataReader转换成List<T>的方法的具体代码 但实际中从来没用过,因为除非一个表对应一个数据处理对象,这样使用太麻烦.
游离失所 2014-06-17
  • 打赏
  • 举报
回复
引用 11 楼 ruanwei1987 的回复:
反射 影响性能没你说的那么大吧, 反射这个时候不用,不浪费了
很同意啊。。这里不用真是太浪费了
阿布说不 2014-06-17
  • 打赏
  • 举报
回复
以上测试四次,数据写反 校正后 经测试:测试表26个字段, 关联多表, 使用Top分页查询 ,查询条件若干, 表内数据有35544行 条件:同样的查询语句 方式一:查询出来SQLDataReader,反射转换成实体 方式二:查询出DataSet后,转成实体,动软代码生成器生成 测试一,查询30000条数据 第一次 方式一:00:00:01.7665529 方式二:00:00:02.1337727 第二次 方式一:00:00:01.9709384 方式二:00:00:02.0881764 第三次 方式一:00:00:02.0682092 方式二:00:00:02.2194930 测试二,查询10000条数据 第一次 方式一:00:00:00.7132845 方式二:00:00:00.8282455 第二次 方式一:00:00:00.6753422 方式二:00:00:00.8400506 第三次 方式一:00:00:00.8196474 方式二:00:00:00.7633793 测试三,查询1000条数据 第一次 方式一:00:00:00.2085212 方式二:00:00:00.1651695 第二次 方式一:00:00:00.1602685 方式二:00:00:00.3051809 第三次 方式一:00:00:00.1931657 方式二:00:00:00.2271651 测试四,查询100条数据 第一次 方式一:00:00:00.1375071 方式二:00:00:00.0755168 第二次 方式一:00:00:00.0925081 方式二:00:00:00.1342994 第三次 方式一:00:00:00.2421885 方式二:00:00:00.1381772 反射没想象中的那么差
阿布说不 2014-06-17
  • 打赏
  • 举报
回复
经测试:测试表26个字段, 关联多表, 使用Top分页查询 ,查询条件若干, 表内数据有35544行 条件:同样的查询语句 方式一:查询出来SQLDataReader,反射转换成实体 方式二:查询出DataSet后,转成实体,动软代码生成器生成 测试一,查询30000条数据 第一次 方式一:00:00:01.7665529 方式二:00:00:02.1337727 第二次 方式一:00:00:01.9709384 方式二:00:00:02.0881764 第三次 方式一:00:00:02.0682092 方式二:00:00:02.2194930 测试二,查询10000条数据 第一次 方式一:00:00:00.7132845 方式二:00:00:00.8282455 第二次 方式一:00:00:00.6753422 方式二:00:00:00.8400506 第三次 方式一:00:00:00.8196474 方式二:00:00:00.7633793 测试三,查询1000条数据 第一次 方式一:00:00:00.2085212 方式二:00:00:00.1651695 第二次 方式一:00:00:00.1602685 方式二:00:00:00.3051809 第三次 方式一:00:00:00.1931657 方式二:00:00:00.2271651 测试四,查询100条数据 第一次 方式一:00:00:00.2498874 方式二:00:00:00.2768622 第二次 方式一:00:00:00.1331417 方式二:00:00:00.1483081 第三次 方式一:00:00:00.0927226 方式二:00:00:00.1846782 反射没想象中的那么差
紫魂一号 2014-06-17
  • 打赏
  • 举报
回复
sql 都来反射....老老实实 一个个的写咯,查询sql 灵活多变,你这样写完全是多此一举....我倒是比较喜欢用枚举+实体类的方式去构造sql
ojekleen 2014-06-05
  • 打赏
  • 举报
回复
反射生成实体确实是一劳永逸的事情,但是实体就必须与表结构对应完整,而业务上可能会有很多不同需求,其实相对应于反射,ORM更消耗性能。
ojekleen 2014-06-05
  • 打赏
  • 举报
回复
1:反射性能不是想象的那么差。 2:你只是在生成实体的时候进行反射,和使用XML保存Sql语句没啥大关系 吧?
tinydyw 2014-06-05
  • 打赏
  • 举报
回复
你没有考虑DBNull...也没有考虑数据库里面有个东西叫自定义类型.....最后你的实体类也没有真实反映数据库的结构... 嘛..反正现在的硬件越来越发达...不拿到真实环境里试试 你怎么可能知道方案是否合适....反射影响性能也不至于那么恐怖..毕竟c#是以第1次访问慢 后面就会比较快为特点的...
阿布说不 2014-06-05
  • 打赏
  • 举报
回复
引用 20 楼 tinydyw 的回复:
获得实体属性和赋值我这里有代码...不过是我自己写的..你自己测试看看有没有问题....

        /// <summary>
        /// 根据数据行和类型初始化实例的方法
        /// 请注意这里的命名规范,主键表的主键为"Id",外键表的外键为外键表名+"Id"
        /// 如果不同,则会引发SqlException
        /// </summary>
        /// <param name="dr"></param>
        /// <returns></returns>
        public object Intitial(DataRow dr, Type type)
        {
            ///初始化所需要的属性,包括:
            ///
            ///实例化的引用,此处为t
            ///实例化对象的属性数组,此处引用为prs

            object t = Assembly.Load(type.Assembly.FullName).CreateInstance(type.Namespace + "." + type.Name);

            PropertyInfo[] prs = t.GetType().GetProperties();

            ///循环为实例对象的属性赋值
            for (int i = 0; i < prs.Length; i++)
            {
                ///基元类型,基元类型的可空扩展类型,string,datetime,decimal类型可以直接获取属性值
                if (t.GetType().GetProperty(prs[i].Name).PropertyType.IsPrimitive
                    || t.GetType().GetProperty(prs[i].Name).PropertyType.Name == "String"
                    || t.GetType().GetProperty(prs[i].Name).PropertyType.Name == "DateTime"
                    || t.GetType().GetProperty(prs[i].Name).PropertyType.Name == "Decimal"
                    || t.GetType().GetProperty(prs[i].Name).PropertyType.Name.Contains("Nullable"))
                {
                    t.GetType().GetProperty(prs[i].Name).SetValue(t, dr[prs[i].Name], null);
                }

                ///其他类型则重新编写Sql语句查询其外键表,实例化对象后赋值
                else
                {
                    ///拼写Sql语句
                    SqlStrB.Clear();
                    SqlStrB.AppendFormat("select * from [{0}] where Id=@id", prs[i].Name);

                    ///初始化参数数组
                    SqlParameter[] para = new SqlParameter[1];
                    para[0] = new SqlParameter("@id", dr[prs[i].Name + "Id"]);

                    ///查询所需的外键数据
                    DataRow row = new DBHelper().Search(SqlStrB.ToString(), para).Rows[0];

                    ///为相应的属性实例化并赋值
                    if (row != null)
                    {
                        t.GetType().GetProperty(prs[i].Name).SetValue(t, Intitial(row, prs[i].PropertyType), null);
                    }

                    ///未获取时引发异常,异常信息为"数据检索失败,请检查Sql语句及参数!"
                    else
                    {
                        throw new Exception("数据检索失败,请检查Sql语句及参数!");
                    }
                }
            }
            return t;
        }

        /// <summary>
        /// 获取实例的单一属性值
        /// </summary>
        /// <param name="model">model为该实例</param>
        /// <param name="pro">pro为要获取值的属性</param>
        /// <returns></returns>
        protected dynamic GetProValue(object model, PropertyInfo pro)
        {
            ///若属性类型为.net的基元类型或String类型、DateTime类型与Decimal类型,则直接返回属性值
            if (pro.PropertyType.IsPrimitive
                || pro.PropertyType.Name == "String"
                || pro.PropertyType.Name == "DateTime"
                || pro.PropertyType.Name == "Decimal")
            {
                return model.GetType().GetProperty(pro.Name).GetValue(model, null);
            }

            ///否则创建相应实例,用递归给实例属性赋值并返回实例
            else
            {
                ///获取属性类型的程序集
                Assembly ass = pro.PropertyType.Assembly;

                ///获取属性类型的命名空间
                string nameSpace = pro.PropertyType.Namespace;

                ///获取属性类型的类名
                string className = pro.PropertyType.Name;

                ///创建该类实例
                dynamic result = Assembly.Load(ass.FullName)
                    .CreateInstance(nameSpace + "." + className);
                
                ///获取实例的属性数组
                PropertyInfo[] prs = result.GetType().GetProperties();

                ///用递归循环给实例的属性赋值
                for (int i = 0; i < result.GetType().GetProperties().Length; i++)
                {
                    result.GetType().GetProperty(prs[i].Name).SetValue(result, GetProValue(model.GetType().GetProperty(result.GetType().Name).GetValue(model, null), prs[i]), null);
                }

                ///返回实例
                return result;
            }
        }
        
注意你的实体类里外键属性命名必须是外键表+id..当然如果你有其他规范 你就自己改吧...
       #region 把SQLDataReader转成Class一条
        private static T ConvertOneClass<T>(Type tp, SqlDataReader sdr)
        {
            try
            {
                T t = System.Activator.CreateInstance<T>();
                for (int i = 0; i < sdr.FieldCount; i++)
                {
                    if (sdr.IsDBNull(i))
                    {
                        continue;
                    }
                    string ColName = sdr.GetName(i);
                    object ColValue = sdr.GetValue(i);
                    if (ColValue == null)
                    {
                        continue;
                    }
                    PropertyInfo pi = tp.GetProperty(ColName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
                    if (pi != null)
                    {
                        if (pi.CanWrite)
                        {
                            if (pi.PropertyType == typeof(Boolean))
                            {
                                if (ColValue.ToString() == "0" || ColValue.ToString() == "1")
                                {
                                    ColValue = ColValue.ToString() == "0" ? false : true;
                                }
                            }
                            try
                            {
                                pi.SetValue(t, ColValue, null);
                            }
                            catch
                            {
                                throw new Exception(ColName + ":转换类型错误,可能原因数据库与实体类型不匹配!");
                            }
                        }
                    }
                }
                return t;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        #endregion

        #region SqlDataReader转成Class
        public static T SDRConvertClass<T>(SqlDataReader sdr)
        {
            try
            {
                Type tp = typeof(T);
                if (sdr.Read())
                {
                    return ConvertOneClass<T>(tp, sdr);
                }
                else
                {
                    return default(T);
                }

            }
            catch (Exception ex)
            {

                throw ex;
            }
        }
        #endregion
阿布说不 2014-06-05
  • 打赏
  • 举报
回复
引用 17 楼 tinydyw 的回复:
使用占位符只能根据类来重新给里面的{0},{1},{2}赋值..从而完成sql语句..你自己还是需要一个个传这些类的属性进去..
但是使用反射...只需要类的属性和数据库字段命名保持一定的规范..那么完全可以直接使用getProperyies()去完成sql语句..根本不需要去管具体是个什么类 类里有什么属性...楼主的方法却是比我的麻烦了很多...xml什么的没必要..如果类的名字能和表名达成一定的规范..那么可以直接使用泛型基类去构建这样的一个通用实现类..
由这个泛型基类的类名和相应的规范拼接数据库表名..然后使用反射构成sql..而且由于是泛型基类..所以如果有对某些表有特殊的需求.可以重载或重写相应的方法完成...


getProperyies()不就是使用的反射吗?Xml只是存储SQL语句的地方,是内嵌文件,只是找起来和写起来方便!
现在问题,我知道使用反射,影响性能,虽然我本地测试,没看出来快慢,但不保证到客户我会死的很惨!
如果有个比较高效的反射方法就可以了,或者代替反射的方法就可以了,不然的话,我服务端都要重写整理
tinydyw 2014-06-05
  • 打赏
  • 举报
回复
哦对了...外键类型必须是外键表对应的实体类...然后上面代码里的SqlStrB是用来拼接sql的stringbuilder对象..
tinydyw 2014-06-05
  • 打赏
  • 举报
回复
获得实体属性和赋值我这里有代码...不过是我自己写的..你自己测试看看有没有问题....

        /// <summary>
        /// 根据数据行和类型初始化实例的方法
        /// 请注意这里的命名规范,主键表的主键为"Id",外键表的外键为外键表名+"Id"
        /// 如果不同,则会引发SqlException
        /// </summary>
        /// <param name="dr"></param>
        /// <returns></returns>
        public object Intitial(DataRow dr, Type type)
        {
            ///初始化所需要的属性,包括:
            ///
            ///实例化的引用,此处为t
            ///实例化对象的属性数组,此处引用为prs

            object t = Assembly.Load(type.Assembly.FullName).CreateInstance(type.Namespace + "." + type.Name);

            PropertyInfo[] prs = t.GetType().GetProperties();

            ///循环为实例对象的属性赋值
            for (int i = 0; i < prs.Length; i++)
            {
                ///基元类型,基元类型的可空扩展类型,string,datetime,decimal类型可以直接获取属性值
                if (t.GetType().GetProperty(prs[i].Name).PropertyType.IsPrimitive
                    || t.GetType().GetProperty(prs[i].Name).PropertyType.Name == "String"
                    || t.GetType().GetProperty(prs[i].Name).PropertyType.Name == "DateTime"
                    || t.GetType().GetProperty(prs[i].Name).PropertyType.Name == "Decimal"
                    || t.GetType().GetProperty(prs[i].Name).PropertyType.Name.Contains("Nullable"))
                {
                    t.GetType().GetProperty(prs[i].Name).SetValue(t, dr[prs[i].Name], null);
                }

                ///其他类型则重新编写Sql语句查询其外键表,实例化对象后赋值
                else
                {
                    ///拼写Sql语句
                    SqlStrB.Clear();
                    SqlStrB.AppendFormat("select * from [{0}] where Id=@id", prs[i].Name);

                    ///初始化参数数组
                    SqlParameter[] para = new SqlParameter[1];
                    para[0] = new SqlParameter("@id", dr[prs[i].Name + "Id"]);

                    ///查询所需的外键数据
                    DataRow row = new DBHelper().Search(SqlStrB.ToString(), para).Rows[0];

                    ///为相应的属性实例化并赋值
                    if (row != null)
                    {
                        t.GetType().GetProperty(prs[i].Name).SetValue(t, Intitial(row, prs[i].PropertyType), null);
                    }

                    ///未获取时引发异常,异常信息为"数据检索失败,请检查Sql语句及参数!"
                    else
                    {
                        throw new Exception("数据检索失败,请检查Sql语句及参数!");
                    }
                }
            }
            return t;
        }

        /// <summary>
        /// 获取实例的单一属性值
        /// </summary>
        /// <param name="model">model为该实例</param>
        /// <param name="pro">pro为要获取值的属性</param>
        /// <returns></returns>
        protected dynamic GetProValue(object model, PropertyInfo pro)
        {
            ///若属性类型为.net的基元类型或String类型、DateTime类型与Decimal类型,则直接返回属性值
            if (pro.PropertyType.IsPrimitive
                || pro.PropertyType.Name == "String"
                || pro.PropertyType.Name == "DateTime"
                || pro.PropertyType.Name == "Decimal")
            {
                return model.GetType().GetProperty(pro.Name).GetValue(model, null);
            }

            ///否则创建相应实例,用递归给实例属性赋值并返回实例
            else
            {
                ///获取属性类型的程序集
                Assembly ass = pro.PropertyType.Assembly;

                ///获取属性类型的命名空间
                string nameSpace = pro.PropertyType.Namespace;

                ///获取属性类型的类名
                string className = pro.PropertyType.Name;

                ///创建该类实例
                dynamic result = Assembly.Load(ass.FullName)
                    .CreateInstance(nameSpace + "." + className);
                
                ///获取实例的属性数组
                PropertyInfo[] prs = result.GetType().GetProperties();

                ///用递归循环给实例的属性赋值
                for (int i = 0; i < result.GetType().GetProperties().Length; i++)
                {
                    result.GetType().GetProperty(prs[i].Name).SetValue(result, GetProValue(model.GetType().GetProperty(result.GetType().Name).GetValue(model, null), prs[i]), null);
                }

                ///返回实例
                return result;
            }
        }
        
注意你的实体类里外键属性命名必须是外键表+id..当然如果你有其他规范 你就自己改吧...
阿布说不 2014-06-05
  • 打赏
  • 举报
回复
引用 15 楼 sp1234 的回复:
如果为了格式化字符串里的一些占位符号(也就是{0}、{1}、{2}),那么根本用不着反射。比如说人家原本可以写
var sql = string.Format("select * from [{0} where [{1}]='{2}'", obj.tname, obj.searchfield, x);
你偏要写成"select * from [$tname$] where [$searchfield$]='$x$'",而且还要反射,而且还不能清晰地包容这里的x之类的参数,这不是给使用者也找麻烦嘛。
我讨厌这种写SQL方式,太麻烦 如果如下的语句,用StringBuilder拼接,修改起来就麻烦了,当然有些可以自动生成,但如果是多表关联,无法自动生成的语句,写起来怎么办?我这个修改的SQL语句,我只需要把试题Exam这个实体传进去,最后那个,我只需传个参数list<T>,就可以了,就会自动生成一个SqlParameter[]!这里面如何得到实体的属性的?用的反射! sql语句查询到的无论是DataSet还是SqlDataReader,怎么把值赋给泛型T,还用的还是反射! 这里面两点用到反射的地方,如果有更好的方法,可以替换就好了! 虽然这样省了很多代码,但是大量使用反射,所以我也考虑把我的服务端全部重写! 如果重写的话,工作量有点大! 而且C/S架构,服务端,用的WCF服务,服务端不知道用什么框架更合适,希望大神帮帮忙

  <SQL Id="UpdateExam">
    UPDATE dbo.Exam_$TableName$ SET
    E_Year = #Exam.E_Year#,
    E_Area  = #Exam.E_Area#,
    E_Grade = #Exam.E_Grade#,
    E_Diff  = #Exam.E_Diff#,
    E_Kind  = #Exam.E_Kind#,
    E_QuestionTypes = #Exam.E_QuestionTypes#,
    E_FromPaper = #Exam.E_FromPaper#,
    E_Unit = #Exam.E_Unit#,
    E_Owner = #Exam.E_Owner#,
    E_ExtInfo = #Exam.E_ExtInfo#,
    E_State = #Exam.E_State#,
    E_FileIndex = #Exam.E_FileIndex#,
    E_IsObjective = #Exam.E_IsObjective#,
    E_IsObjectiveAnswer = #Exam.E_IsObjectiveAnswer#
    WHERE E_GUID =  #Exam.E_GUID#
  </SQL>
  <SQL Id="SaveMyExam">
    INSERT INTO dbo.MyCollectExam
    (
    E_UserGuid ,
    E_OriginGUID ,
    E_SubjectGUID ,
    E_CollectType
    )
    SELECT * FROM(
    <LOOP Name="list" LinkStr="UNION ALL">
      Select
      '$UserGuid$' E_UserGuid,
      #list[].OriginGUID# E_OriginGUID,
      '$SubjectGUID$' E_SubjectGUID,
      '$MyExamType$' E_CollectType
    </LOOP>
    )a
    WHERE NOT EXISTS(SELECT 1 FROM dbo.MyCollectExam b
    WHERE a.E_UserGuid=b.E_UserGuid
    AND a.E_CollectType=b.E_CollectType
    AND a.E_SubjectGUID=b.E_SubjectGUID
    AND a.E_OriginGUID=b.E_OriginGUID)
  </SQL>
tinydyw 2014-06-05
  • 打赏
  • 举报
回复
另外...obj.searchfield里searchfield不就是反射.?
tinydyw 2014-06-05
  • 打赏
  • 举报
回复
使用占位符只能根据类来重新给里面的{0},{1},{2}赋值..从而完成sql语句..你自己还是需要一个个传这些类的属性进去.. 但是使用反射...只需要类的属性和数据库字段命名保持一定的规范..那么完全可以直接使用getProperyies()去完成sql语句..根本不需要去管具体是个什么类 类里有什么属性...楼主的方法却是比我的麻烦了很多...xml什么的没必要..如果类的名字能和表名达成一定的规范..那么可以直接使用泛型基类去构建这样的一个通用实现类.. 由这个泛型基类的类名和相应的规范拼接数据库表名..然后使用反射构成sql..而且由于是泛型基类..所以如果有对某些表有特殊的需求.可以重载或重写相应的方法完成...
vbfool 2014-06-05
  • 打赏
  • 举报
回复
实体框架搞的方式是缓存反射。
E次奥 2014-06-05
  • 打赏
  • 举报
回复
和数据库操作首先要考虑安全性,不是麻烦的问题!

        public UserLog GetUserLogById(int Id)
        {
            UserLog log = new UserLog();
            string sql = "select * from LogTable where Id=@Id";
            SqlParameter param = new SqlParameter("@Id", Id);
            SqlDataReader dr = SqlHelper.ExecutReader(sql, param);
            if (dr.Read())
            {
                log.Id = Convert.ToInt32(dr["Id"]);
                log.UserName = dr["UserName"].ToString();
                log.UserType = dr["UserType"].ToString();
                log.Operation = dr["Operation"].ToString();
                log.CreateDateTime = dr["CreateDateTime"].ToString();
            }
            dr.Close();
            return log;
        }
小灰狼 2014-06-05
  • 打赏
  • 举报
回复
反射对性能的影响,楼主可以写个程序进行比较,比如写两个循环,执行100万次,一个循环用反射的方式访问对象成员变量,一个直接调用 get; set 属性 个人感觉,如果100万次的差别在10秒之内,都是可以忽略不计的
小灰狼 2014-06-05
  • 打赏
  • 举报
回复
反射对性能影响不大,搞过 java 的人应该知道,java 的很多有名的框架都大量的使用了反射,可以说没有反射就不会有那么多的开源框架。JAVA 的开源框架被人诟病的不少,但是很少有人抱怨因为用了反射而影响性能。 dot net 比 java 迟几年推出,在反射的使用上不应该会比 java 还差;并且 dot net 只在 windows 上运行,不象 java 好样需要考虑不同操作系统之间的差别。
加载更多回复(15)

110,535

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧