夏天的天气就是容易犯困.你看,小菜已经趴在桌上睡着了.
小菜又来回到梦中那熟悉的地方:Discuz公司.
老大看到小菜,说到:小菜啊,孺鸟可教也,现在我给你分配新任务,你收拾一下东西,到数据访问层小组那去,那里正缺人呢.
小菜到数据访问层小组后,与小组成员进行了沟通了解到如下信息.
Discuz!NT2.0 将支持多种数据库 Access,SqlServer,MySql
所以我们设计出来的数据库层架构要合理,而且要方便调用,始终坚持各种数据库的访问方式一致性.
(看来这次任务不简单啊,老大把小菜当大菜使用啊.)
小菜有点而心虚了,好久没有写过访问数据库操作了,都快忘的一干二净了.
不过俺不怕.因为俺有SDK2.0文档.赶紧回去复习一下.
小菜打算先针对Sql数据库进行设计然后再将其扩展成支持多种数据库.
小菜始终认为,心急吃不了热豆腐,我们要步步为营,不管是现在的敏捷开发,还是单元测试,其实讲的都是这个道理.
小菜从小组其它成员那拿到了已经设计好的SQL数据库discuz(小菜也希望在之后的文章中分析该数据库的设计,因为很经典)
表:dnt_forums(论坛版块信息) 这个表被小菜简化了,因为这样更能说明问题
序号 |
字段名 |
类型 |
长度 |
说明 |
1 |
fid |
int |
4 |
论坛fid |
2 |
name |
nchar |
50 |
论坛名称 |
fid |
name |
1 |
版块1 |
2 |
版块2 |
小菜准备先拿这个表练练手感,找回往日的自信.(小菜打算一步一步封装)
1)小菜第一招 管你三七二十一
也就是直接在需要访问数据库的地方,直接访问数据库,丝毫没有封装.代码重复量很大.
小菜打算输出dnt_forums表中的内容
using System.Web;
using System.Data;
using System.Data.SqlClient;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string cmdText = "select fid,name from dnt_forums";
string connString = "Data Source=(local);" +
"User ID=sa;Password=password;Initial Catalog=discuz;Pooling=true";
SqlConnection conn = new SqlConnection(connString);
if (conn.State != ConnectionState.Open)
conn.Open(); //如果连接状态不是打开,则打开连接
SqlCommand cmd = new SqlCommand(cmdText, conn);
SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
//在关闭DataReader时,关联的Connection也将被关闭
while (reader.Read())//循环读取版块名称
{
Response.Write(reader["fid"]);
Response.Write(reader["name"]);
}
reader.Close();//关闭DataReader
reader.Dispose();//释放资源
if (conn.State != ConnectionState.Closed)//如果连接状态不是关闭,则关闭,并释放资源
{
conn.Close();
conn.Dispose();
}
}
}
程序输出: 1版块1 2版块2
虽然是完成了任务,但是如果试想一下,以上代码在我们的论坛中遍地开花.那将是何等的恐怖.
不过还是有一点值得表扬的,代码习惯不错,记住了,用过的要关闭与释放,如SqlConnection等
2)小菜第二招 人老了记性不好怎么办.
合理的利用using,as,is,params会有意想不到的效果.
小菜最近记性不太好,打开了SqlConnecion等,老是放了关.有没有比较好的方法呢?
有: using 英文就是使用的意思,使用过后便会自己关闭与释放所使用的资源.
using System.Web;
using System.Data;
using System.Data.SqlClient;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string cmdText = "select fid,name from dnt_forums";
string connString = "Data Source=(local);" +
"User ID=sa;Password=password;Initial Catalog=discuz;Pooling=true";
using (SqlConnection conn = new SqlConnection(connString))
{
if (conn.State != ConnectionState.Open)
conn.Open(); //如果连接状态不是打开,则打开连接
SqlCommand cmd = new SqlCommand(cmdText, conn);
using (SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
while (reader.Read())//循环读取
{
Response.Write(reader["fid"]);
Response.Write(reader["name"]);
}
}
}
}
}
程序输出: 1版块1 2版块2
小菜刚在沾沾自喜,这时老大走了过来,看看小菜有没有进步.看到小菜的代码说到.
小菜啊!如果传入新页面一个fid,让你取出版块名称.你打算怎么做啊?
<a href="GetForumName.aspx?fid=1">取出版块名称的页面</a>
小菜心中暗喜.这有什么难的,要在老大面前展示展示.
using System.Web;
using System.Data;
using System.Data.SqlClient;
public partial class GetForumName : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
int fid = Convert.ToInt32(Request.Params["fid"]);//获取传入参数fid
string cmdText = "select name from dnt_forums where fid=@fid";
string connString = "Data Source=(local);" +
"User ID=sa;Password=password;Initial Catalog=discuz;Pooling=true";
using (SqlConnection conn = new SqlConnection(connString))
{
if (conn.State != ConnectionState.Open)
conn.Open(); //如果连接状态不是打开,则打开连接
SqlParameter parm = new SqlParameter("@fid", SqlDbType.Int, 4);//设置Sql参数fid
parm.Value = fid;
SqlCommand cmd = new SqlCommand(cmdText, conn);
cmd.Parameters.Add(parm);
string name = cmd.ExecuteScalar().ToString();
Response.Write(name);
}
}
}
程序正常输出:版块1
小菜心中又想不对啊,自言自语到,代码重复好多,这不是和之前的配置处理时遇到的问题一样嘛.
这才想到,看来老大是在考我. 老大满意的走了..
3)小菜第三招 小菜小封装
现在很多的开源代码如果没有使用ORM,基本上都会看到 数据库访问帮助类如SqlHelper.
小菜也打算自己弄个SqlHelper来封装对Sql数据库的访问操作.看来我们的代码质量又得到了一定的提高.那我们就继续努力吧,把SqlHelper改造成功能强大的Sql数据库操作类吧.
using System.Data;
using System.Data.SqlClient;
namespace Discuz.Data
{
public class SqlHelper
{
private static string m_connString = "Data Source=(local);" +
"User ID=sa;Password=password;Initial Catalog=discuz;Pooling=true"; //Sql数据库连接字符串
/// <summary>
/// 执行一个SqlCommand返回一个记录集
/// </summary>
/// <remarks>
/// 例如:
/// SqlDataReader r = ExecuteReader(CommandType.Text, "select id,name from dnt_forums", null);
/// </remarks>
/// <param name="cmdType">SqlCommand类型 如存储过程或Sql文本命令</param>
/// <param name="cmdText">SqlCommand文本 如存储过程名称或Sql语句</param>
/// <param name="cmdParms">SqlCommand的参数SqlParameters</param>
/// <returns>一个包含记录的SqlDataReader</returns>
public static SqlDataReader ExecuteReader(CommandType cmdType, string cmdText, params SqlParameter[] cmdParms)
{
SqlCommand cmd = new SqlCommand();
SqlConnection conn = new SqlConnection(m_connString);
try
{
PrepareCommand(cmd, conn, cmdType, cmdText, cmdParms);
SqlDataReader rdr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
cmd.Parameters.Clear();
return rdr;
}
catch(Exception ex)
{
conn.Close();
throw ex;
}
}
/// <summary>
/// 执行一个SqlCommand并返回第一条记录的第一列的值
/// </summary>
/// <remarks>
/// 例如:
/// SqlParameter parm = new SqlParameter("@fid", SqlDbType.Int, 4);
/// parm.Value = fid;
/// Object obj = ExecuteScalar(CommandType.Text, "select name from dnt_forums where fid=@fid", parm);
/// </remarks>
/// <param name="cmdType">SqlCommand类型 如存储过程或Sql文本命令</param>
/// <param name="cmdText">SqlCommand文本 如存储过程名称或Sql语句</param>
/// <param name="cmdParms">SqlCommand的参数SqlParameters</param>
/// <returns>返回第一条记录的第一列的值</returns>
public static object ExecuteScalar(CommandType cmdType, string cmdText, params SqlParameter[] cmdParms)
{
SqlCommand cmd = new SqlCommand();
using (SqlConnection conn = new SqlConnection(m_connString))
{
PrepareCommand(cmd, conn, cmdType, cmdText, cmdParms);
object val = cmd.ExecuteScalar();
cmd.Parameters.Clear();
return val;
}
}
/// <summary>
/// 准备一个SqlCommand用来执行
/// </summary>
/// <param name="cmd">SqlCommand对象</param>
/// <param name="conn">SqlConnection对象</param>
/// <param name="cmdType">SqlCommand类型 如存储过程或Sql文本命令</param>
/// <param name="cmdText">SqlCommand文本 如存储过程名称或Sql语句</param>
/// <param name="cmdParms">SqlCommand的参数SqlParameters</param>
private static void PrepareCommand(SqlCommand cmd, SqlConnection conn, CommandType cmdType, string cmdText, SqlParameter[] cmdParms)
{
if (conn.State != ConnectionState.Open)
conn.Open();
cmd.Connection = conn;
cmd.CommandText = cmdText;
cmd.CommandType = cmdType;
if (cmdParms != null)
{
foreach (SqlParameter parm in cmdParms)
cmd.Parameters.Add(parm);
}
}
}
}
那接下来,就让我们来享受这个SqlHelper帮助类的成果吧.调用它吧.
遍历dnt_forums,输出fid和name
using System.Web;
using System.Data;
using System.Data.SqlClient;
using Discuz.Data;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string cmdText = "select fid,name from dnt_forums";
using (SqlDataReader reader = SqlHelper.ExecuteReader(CommandType.Text, cmdText, null))
{
while (reader.Read())
{
Response.Write(reader["fid"]);
Response.Write(reader["name"]);
}
}
}
}
安fid取出name版块名
using System.Web;
using System.Data;
using System.Data.SqlClient;
using Discuz.Data;
public partial class GetForumName : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
int fid = Convert.ToInt32(Request.Params["fid"]);//获取传入参数fid
string cmdText = "select name from dnt_forums where fid=@fid";
SqlParameter parm = new SqlParameter("@fid", SqlDbType.Int, 4);//设置Sql参数fid
parm.Value = fid;
object name = SqlHelper.ExecuteScalar(CommandType.Text, cmdText, parm);
Response.Write(name);
}
}
嗯,感觉不错,我们的代码质量得到了很大的提高了.
但我们的SqlHelper还少了个很常用的功能ExecuteNonQuery
using System.Data;
using System.Data.SqlClient;
namespace Discuz.Data
{
public class SqlHelper
{
private static string m_connString = "Data Source=(local);" +
"User ID=sa;Password=password;Initial Catalog=discuz;Pooling=true"; //Sql数据库连接字符串
/// <summary>
/// 执行一个SqlCommand返回受该SqlCommand影响的行数
/// </summary>
/// <remarks>
/// 例如:
/// SqlParameter parm = new SqlParameter("@fid", SqlDbType.Int, 4);
/// parm.Value = fid;
/// int result = ExecuteNonQuery(CommandType.Text, "delete from dnt_forums where fid=@fid", parm);
/// </remarks>
/// <param name="cmdType">SqlCommand类型 如存储过程或Sql文本命令</param>
/// <param name="cmdText">SqlCommand文本 如存储过程名称或Sql语句</param>
/// <param name="cmdParms">SqlCommand的参数SqlParameters</param>
/// <returns>返回受SqlCommand影响的行数</returns>
public static int ExecuteNonQuery(CommandType cmdType, string cmdText, params SqlParameter[] cmdParms)
{
SqlCommand cmd = new SqlCommand();
using (SqlConnection conn = new SqlConnection(m_connString))
{
PrepareCommand(cmd, conn, cmdType, cmdText, cmdParms);
int val = cmd.ExecuteNonQuery();
cmd.Parameters.Clear();
return val;
}
}
其它同前面,略
}
}
4) 小菜第四招 细细体会.
以上SqlHelper类,为微软PetShop4.0中的代码,写的比较好.
这时小菜被老娘拍醒了,,还睡呢,,太阳晒屁股了,,,快起来.....
休息,休息,,,,下篇继续努力.