zoukankan      html  css  js  c++  java
  • CYQ.Data 轻量数据层之路 SQLHelper 回头太难(八)

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

    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么

    只能说,一切回头太难。

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

    版权声明:本文原创发表于 博客园,作者为 路过秋天 本文欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则视为侵权。
    个人微信公众号
    创业QQ群:617713515
    Donation(扫码支持作者):支付宝:
    Donation(扫码支持作者):微信:
  • 相关阅读:
    记第一次为开源代码报漏洞
    入职第三周——总结前两周学习内容
    入职一星期之感想
    毕业季之礼
    基于mint-ui的移动应用开发案例二(项目搭建)
    基于mint-ui的移动应用开发案例一(简介)
    工作笔记一——杂项
    微信小程序实战小小应用——豆瓣电影
    React学习之坑(二)- 基础入门
    React学习笔记(一)- 环境搭建
  • 原文地址:https://www.cnblogs.com/cyq1162/p/1807104.html
Copyright © 2011-2022 走看看