CYQ.Data 轻量数据层之路 SQLHelper 回头太难(八)

    xiaoxiao2022-07-09  200

    提前说明:正如网友反映的一样,为了不至于产生明显的误导,特别加了此首段说明

    SQLHelper,几乎是每个过来者必经的阶段,写好一个SQLHelper是非常重要的一环,所以希望年轻的来者,要多加实践,别只看不动手,哪怕照着写一写,也是相当的有益。 对于本框架系列,希望年轻来者在掌握使用的同时,动手照着系列文章写一写,如果照着写出来的,相信成长不是一点半点的;别光看不练,最后只能忽悠却动不了手。

     

     

     

    这篇文章很不好写,我在电脑前思索了一天,也不知怎么下手。

    关于SQLHelper的文章遍地都是,写的不咋的随时被拍砖,不写吧,本系列又不完整,所以,买了个保险之后,低调点写了。

     

    从哪写起呢?直接把整个SQLHelper类复制一下,文章就算写完了?好像其它遍地都是的文章都差不多是这个样子的。

     

    在还没写完这篇时,曾经有那么个热心人士反编绎过我的框架,还洒了点代码出来了,提前爆光了一下:

    详见:

    1:CYQ.Data 轻量数据层之路 华丽升级 V1.3出世(五)

    2:CYQ.Data 轻量数据层之路 应用示例二 在线聊天(六)

     

    本文停了一天没动了,现在重新执笔动手了,想了想,于是在博客园搜了一下,看了第一页搜出来的10篇SQLHelper相关代码,

    简略看了一眼,发现还是鄙人的简洁友好的多,于是,继续写下来了:

     

    其实我们要的SQLHelper很简单,只要能执行下sql语句和存储过程,也就这个样了,至于事务,这里先放一边了。

    接着一步一脚印:

    1:我们新增加一个SQHelper类,由于本类并不对外开放,所以我们不改修饰符为public,默认就好了

         ///   <summary>      ///  SQLHelper by 路过秋天      ///   </summary>      class  SQLHelper     {     }

     

    2:由于我们不做成静态方法调用方式,所以我们需要实例化,添加两个构造函数

      ///   <summary>      ///  SQLHelper by 路过秋天      ///   </summary>      class  SQLHelper     {          ///   <summary>          ///  默认配置连接字符串名:Conn          ///   </summary>          public  SQLHelper()         {                     }          ///   <summary>          ///  可以传链接字符串          ///   </summary>            public  SQLHelper( string  conn)         {                     }     }

     

    3:既然要实例化才调用,那我们只需要一个Command和一个链接就可以了,所以,我们把它们拿到外面定义成全局变量

         class  SQLHelper     {          private  SqlCommand com  =   new  SqlCommand();          private  SqlConnection _con  =   null ;          ///   <summary>          ///  默认配置连接字符串名:Conn          ///   </summary>          public  SQLHelper()         {                     }          ///   <summary>          ///  可以传链接字符串          ///   </summary>          public  SQLHelper( string  conn)         {                     }     }

    上面没有直接new 出SqlConnection,是因为它和链接字符串相关,留到构造函数里初始化了。

     

    4:实现构造函数,初始化SqlConnection

             public  SQLHelper()         {              if  (ConfigurationManager.ConnectionStrings[ " Conn " !=   null )             {                 _con  =   new  SqlConnection(ConfigurationManager.ConnectionStrings[ " Conn " ].ConnectionString);                 com.Connection  =  _con;             }         }          public  SQLHelper( string  conn)         {              if  (conn.Length  <   25 )             {                 conn  =  ConfigurationManager.ConnectionStrings[conn].ConnectionString;             }             _con  =   new  SqlConnection(conn);             com.Connection  =  _con;         }

     默认用webconfig配置Conn,同时上面根据传入的长度,来判断是从配置文件传入,还是直接的链接字符串!

     

    5:我们增加一个全局的成员属性,一个是否记录异常,如果不记录则会抛出异常

    public   bool  WriteLog  =   true ;

     

    6:我这里另开一个Log类,来处理异常,先留下一个静态空方法,回头处理

    class  Log {          public   static   void  WriteLog( string  message)         {               // ...待实现...         } }

     

    7:要执行SQL语句或存储过程,免不了要打开和关闭链接,这里封装成方法,加try

            private   void  OpenCon()         {              try             {                  if  (_con.State  ==  ConnectionState.Closed)                 {                     _con.Open();                 }             }              catch  (SqlException err)             {                  if  (WriteLog)                 {                     Log.WriteLog(err.Message);                 }             }         }          private   void  CloseCon()         {              try             {                  if  (_con.State  ==  ConnectionState.Open)                 {                     _con.Close();                 }             }              catch  (SqlException err)             {                  if  (WriteLog)                 {                     Log.WriteLog(err.Message);                 }             }         }

    我们在打开和关闭异时,调用了日志记录功能。

     

    8:我们继承IDisposable接口,完成资源释放

    class  SQLHelper:IDisposable {          // ...省略N行...          #region  IDisposable 成员           public   void  Dispose()         {              if  (_con  !=   null )             {                 CloseCon();                 _con  =   null ;             }              if  (com  !=   null )             {                 com  =   null ;             }         }          #endregion }

     

     

    9:我们封装一下SqlCommand的几个执行返回

             internal   int  ExeNonQuery( string  procName,  bool  isProc)         {              // 更新删除操作,返回受影响行数          }          internal   object  ExeScalar( string  procName, bool isProc)         {              // 返回首行首列的单个记录          }          internal  SqlDataReader ExeDataReader( string  procName,  bool  isProc)         {              // 返回读取流          }          internal  DataTable ExeDataTable( string  procName,  bool  isProc)         {              // 返回DataTable,本框架没用到,因为有了MDataTable         }

     

    由于存储过程和单独的sql语句混在一起,我加了一个函数来处理这些共同的事情:

             private   void  SetCommandText( string  commandText,  bool  isProc)         {             com.CommandText  =  commandText;             com.CommandType  =  isProc  ?  CommandType.StoredProcedure : CommandType.Text;              if  ( ! com.Parameters.Contains( " ReturnValue " ))             {                 com.Parameters.Add( " ReturnValue " , SqlDbType.Int).Direction  =  ParameterDirection.ReturnValue;             }         }

    后面附加了一个常用的返回值参数ReturnValue。

     

    10:实现封装的几个方法,异常则记录日志/抛出

             internal   int  ExeNonQuery( string  procName,  bool  isProc)         {              // 更新删除操作,返回受影响行数             SetCommandText(procName, isProc);              int  rowCount  =   1 ;              try             {                 OpenCon();                 com.ExecuteNonQuery();             }              catch  (SqlException err)             {                 rowCount  =   0 ;                  if  (WriteLog)                 {                     Log.WriteLog(err.Message);                 }             }              return  rowCount;         }          internal   object  ExeScalar( string  procName, bool isProc)         {              // 返回首行首列的单个记录             SetCommandText(procName, isProc);              object  returnValue  =   null ;              try             {                 OpenCon();                 returnValue  =  com.ExecuteScalar();             }              catch  (SqlException err)             {                  if  (WriteLog)                 {                     Log.WriteLog(err.Message);                 }             }              return  returnValue;         }          internal  SqlDataReader ExeDataReader( string  procName,  bool  isProc)         {              // 返回读取流             SetCommandText(procName, isProc);             SqlDataReader sdr  =   null ;              try             {                 OpenCon();                 sdr  =  com.ExecuteReader(CommandBehavior.CloseConnection);                  if  (sdr  !=   null   &&   ! sdr.HasRows)                 {                     sdr.Close();                     sdr  =   null ;                 }             }              catch  (SqlException err)             {                  if  (WriteLog)                 {                     Log.WriteLog(err.Message);                 }             }              return  sdr;         }          internal  DataTable ExeDataTable( string  procName,  bool  isProc)         {              // 返回DataTable,本框架没用到,因为有了MDataTable             SetCommandText(procName, isProc);             SqlDataAdapter sdr  =   new  SqlDataAdapter(com);             DataTable dataTable  =   new  DataTable();              try             {                 OpenCon();                 sdr.Fill(dataTable);             }              catch  (SqlException err)             {                  if  (WriteLog)                 {                     Log.WriteLog(err.Message);                 }             }              finally             {                 sdr.Dispose();             }              return  dataTable;         }

     

     

    11:参数方法,无论是存储过程,还是传参型的sql语句,都要用到

    A:参数增加:

             internal   void  AddParameters( string  parameterName,  object  value)         {              if  ( ! com.Parameters.Contains(parameterName))             {                 com.Parameters.AddWithValue(parameterName, value);             }         }          internal   void  AddParameters( string  parameterName,  object  value, SqlDbType sqlDbType)         {              if  ( ! com.Parameters.Contains(parameterName))             {                 com.Parameters.Add(parameterName, sqlDbType).Value  =  value;             }         }

     

    B:参数清除:

             internal   void  ClearParameters()         {              if  (com  !=   null   &&  com.Parameters  !=   null )             {                 com.Parameters.Clear();             }         }

     

    12:返回值属性,一般用于返回记录总数

             private   int  returnValue;          public   int  ReturnValue         {              get             {                  if  (com  !=   null   &&  com.Parameters  !=   null )                 {                   int .TryParse(Convert.ToString(com.Parameters[ " ReturnValue " ].Value), out  returnValue);                 }                  return  returnValue;             }              set  { returnValue  =  value; }         }

    就此,SQLHelper 就算写完了,余下要处理一下日志记录与异常抛出。

     

    13:Log类异常日志记录与抛出

    class  Log {          public   static   void  WriteLog( string  message)         {              if  (IsCanWrite())             {                   InsertLogToData(message);             }              else             {                  throw   new  Exception(message);             }         }          private   static   bool  IsCanWrite()         {             // 从配置文件取          }          private   static   void  InsertLogToData( string  message)         {              // 错误日志入库,可以自定义写文本或入数据库          } }

    判断是一下是否设置为可写日志,如果是则写,否则抛异常。

     

    14:实现IsCanWrite方法

    private   static   bool  IsCanWrite() {       bool  IsCanWriteLog;       bool .TryParse(Convert.ToString(ConfigurationManager.AppSettings[ " IsWriteLog " ]),  out  IsCanWriteLog);       return  IsCanWriteLog; }

    从配置文件里取配置,如果配置为true,就记录日志,否则抛出异常。

     

    插曲:写到这里,博客园又挂了,好在有预感之前保存了一下文章:上图:

    十几分钟后,正常了!!!

     

    15:实现InsertLogToData方法,将错误异常入库

             private   static   void  InsertLogToData( string  message)         {              // 错误日志入库,可以自定义写文本或入数据库              if  (ConfigurationManager.ConnectionStrings[ " LogConn " ==   null )             {                  return   ;             }              string  pageUrl  =  System.Web.HttpContext.Current.Request.Url.ToString();             SQLHelper helper  =   new  SQLHelper( " LogConn " );             helper.WriteLog  =   false ; // 再产生错误就不写日志了,不能产生死循环              try             {                 helper.AddParameters( " @PageUrl " , pageUrl, System.Data.SqlDbType.NVarChar);                 helper.AddParameters( " @ErrorMessage " , message, System.Data.SqlDbType.NVarChar);                 helper.ExeNonQuery( " insert into ErrorLogs(PageUrl,ErrorMessage) values(@PageUrl,@ErrorMessage) " false );                 helper.Dispose();             }              catch             {                 // 啥也不做了             }         }

    这里只是对ErrorLogs表进行插入数据操作,将当前发生错误的Url地址及错误信息插入表中。

    同时也演示了一把SQLHelper的用法。当然你可以修改成自己需要的日志记录方法。

     

    至此,想了一天,终于把这个SQLHelper类给写完了,虽然此类在本框架中不对外开放使用,不过有心的读者仍可以独立出去使用。

     

    OK,本节也就到此结束了。欢迎读者留言讨论或提出建议!

     

    后语:从你学会写或用SQLHelper那时起,你还会去界面写一堆的类似这样的代码吗?

            SqlConnection con  =   new  SqlConnection( " server=.;database=CYQ;uid=sa;pwd=123456 " );         SqlCommand com  =   new  SqlCommand();         com.Connection  =  con;         com.CommandType  =  CommandType.Text;         com.CommandText  =   " SELECT * FROM CYQTABLE WHERE ID=888 " ;         con.Open();         SqlDataReader sdr = com.ExecuteReader();          if  (sdr  !=   null )         {              while  (sdr.HasRows)             {                 sdr.Read();                  // ...循环读...             }             sdr.Close();         }         con.Close();

     

    难了吧,回头已太难了,不管是基于认知度的提升还是开发效率上,你都基本不回头了吧。

     

    如果有一天

    你又认知了微软的ADO.NET Entity Framework或是NHibernate或其它框架。

    又或者有一天

    你原创框架了

    又或是使用本框架了

    你还会回头用那个曾经过的SQLHelper么

    只能说,一切回头太难。

     

    备注:完整框架源码会在本系列结束之后另开章节发布,暂时望勿激动,学习思想才是重要的。

    版权声明:本文原创发表于博客园,作者为路过秋天,原文链接:

    http://www.cnblogs.com/cyq1162/archive/2010/08/25/1807104.html

    相关资源:jd_seckill京东抢茅台插件最新版【京东飞天茅台1499抢购】Python脚本的完整安装 使用教程
    最新回复(0)