当你看到那一堆烦人的数据库操作的时候,是不是有一种想杀人的感觉呢。一定要淡定呀。
要对这个世界充满信心的。生活这么美好,毕竟还是有很多美女的。
封装是面向对象的一个重要思想。微软大爷提供了数据库通用操作类。就是来解决这个问题的。就是将一些通用的数据库操作封装到一个类中。实现代码的复用。这样,在DAL的类中就再也看不到讨厌的数据库连接啦。哈哈。
新建一个项目,命名为DBUtility,意思是数据库通用模块。
删除自动生成的class1.cs,在里面添加一个SQLHelper类。
微软有提供这样的类。源自微软的示例项目PetShop。我现在就是使用PetShop里的这个类。为了简明易懂,我将原版中的数据库命令参数缓存功能和关于事物的那个部分去掉了。并且发挥了我超人的英语屎力,翻译了一下原有的代码备注。(翻译的很烂,英语大牛莫拿板砖砸我)。
SQLHelper内容如下:
using System;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Collections;
namespace DBUtility
{
/// <summary>
/// SqlHelper类的封装是为了取得高性能,包含可扩展的最常用的一些功能
/// </summary>
public abstract class SqlHelper
{
//数据库连接字符串
public static readonly string ConnectionStringLocalTransaction = ConfigurationManager.ConnectionStrings["SQLConnString"].ConnectionString;
/// <summary>
/// 根据指定的数据库连接字符串以及提供的参数,执行一个SqlCommand(没有返回结果集)
/// </summary>
/// <remarks>
/// 例如:
/// int result = ExecuteNonQuery(connString, CommandType.StoredProcedure, "PublishOrders", new SqlParameter("@prodid", 24));
/// </remarks>
/// <param name="connectionString">一个有效的数据库连接字符串</param>
/// <param name="commandType">命令类型(存储过程,文本等)</param>
/// <param name="commandText">存储过程名或T-SQL命令</param>
/// <param name="commandParameters">一个存放执行Sql命令的参数的数组</param>
/// <returns>一个整数,表示受影响的行数</returns>
public static int ExecuteNonQuery(string connectionString, CommandType cmdType, string cmdText, params SqlParameter[] commandParameters)
{
SqlCommand cmd = new SqlCommand();
using (SqlConnection conn = new SqlConnection(connectionString))
{
PrepareCommand(cmd, conn, cmdType, cmdText, commandParameters);
int val = cmd.ExecuteNonQuery();
cmd.Parameters.Clear();
return val;
}
}
/// <summary>
/// 根据提供的参数,执行一个SqlCommand(没有返回结果集)
/// </summary>
/// <remarks>
/// 例如:
/// int result = ExecuteNonQuery(connString, CommandType.StoredProcedure, "PublishOrders", new SqlParameter("@prodid", 24));
/// </remarks>
/// <param name="conn">一个已经存在的数据库连接</param>
/// <param name="commandType">命令类型(存储过程,文本等)</param>
/// <param name="commandText">存储过程名或T-SQL命令</param>
/// <param name="commandParameters">一个存放执行Sql命令的参数的数组</param>
/// <returns>一个整数,表示受影响的行数</returns>
public static int ExecuteNonQuery(SqlConnection connection, CommandType cmdType, string cmdText, params SqlParameter[] commandParameters)
{
SqlCommand cmd = new SqlCommand();
PrepareCommand(cmd, connection, cmdType, cmdText, commandParameters);
int val = cmd.ExecuteNonQuery();
cmd.Parameters.Clear();
return val;
}
/// <summary>
/// 根据指定的数据库连接字符串以及提供的参数,执行一个SqlCommand,并返回结果
/// </summary>
/// <remarks>
/// 例如:
/// SqlDataReader r = ExecuteReader(connString, CommandType.StoredProcedure, "PublishOrders", new SqlParameter("@prodid", 24));
/// </remarks>
/// <param name="connectionString">一个有效的数据库连接字符串</param>
/// <param name="commandType">命令类型(存储过程,文本等)</param>
/// <param name="commandText">存储过程名或T-SQL命令</param>
/// <param name="commandParameters">一个存放执行Sql命令的参数的数组</param>
/// <returns>一个包含结果集的SqlDataReader</returns>
public static SqlDataReader ExecuteReader(string connectionString, CommandType cmdType, string cmdText, params SqlParameter[] commandParameters)
{
SqlCommand cmd = new SqlCommand();
SqlConnection conn = new SqlConnection(connectionString);
try
{
PrepareCommand(cmd, conn, cmdType, cmdText, commandParameters);
SqlDataReader rdr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
cmd.Parameters.Clear();
return rdr;
}
catch
{
conn.Close();
throw;
}
}
/// <summary>
/// 根据指定的数据库连接字符串以及提供的参数,执行一个SqlCommand,返回记录的第一行第一列数据
/// </summary>
/// <remarks>
/// 例如:
/// Object obj = ExecuteScalar(connString, CommandType.StoredProcedure, "PublishOrders", new SqlParameter("@prodid", 24));
/// </remarks>
/// <param name="connectionString">一个有效的数据库连接字符串</param>
/// <param name="commandType">命令类型(存储过程,文本等)</param>
/// <param name="commandText">存储过程名或T-SQL命令</param>
/// <param name="commandParameters">一个存放执行Sql命令的参数的数组</param>
/// <returns>一个需要被转换为所需类型的对象</returns>
public static object ExecuteScalar(string connectionString, CommandType cmdType, string cmdText, params SqlParameter[] commandParameters)
{
SqlCommand cmd = new SqlCommand();
using (SqlConnection connection = new SqlConnection(connectionString))
{
PrepareCommand(cmd, connection, cmdType, cmdText, commandParameters);
object val = cmd.ExecuteScalar();
cmd.Parameters.Clear();
return val;
}
}
/// <summary>
/// 根据提供的参数,执行一个SqlCommand,返回记录的第一行第一列数据
/// </summary>
/// <remarks>
/// 例如:
/// Object obj = ExecuteScalar(connString, CommandType.StoredProcedure, "PublishOrders", new SqlParameter("@prodid", 24));
/// </remarks>
/// <param name="conn">一个已经存在的数据库连接</param>
/// <param name="commandType">命令类型(存储过程,文本等)</param>
/// <param name="commandText">存储过程名或T-SQL命令</param>
/// <param name="commandParameters">一个存放执行Sql命令的参数的数组</param>
/// <returns>一个需要被转换为所需类型的对象</returns>
public static object ExecuteScalar(SqlConnection connection, CommandType cmdType, string cmdText, params SqlParameter[] commandParameters)
{
SqlCommand cmd = new SqlCommand();
PrepareCommand(cmd, connection, cmdType, cmdText, commandParameters);
object val = cmd.ExecuteScalar();
cmd.Parameters.Clear();
return val;
}
/// <summary>
/// 准备一个要执行的命令
/// </summary>
/// <param name="cmd">命令对象</param>
/// <param name="conn">连接对象</param>
/// <param name="cmdType">命令类型, 例如:存储过程,文本等</param>
/// <param name="cmdText">命令内容, 例如:Select * from Products</param>
/// <param name="cmdParms">命令参数</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);
}
}
}
}
在这个数据库辅助类中,使用了ConfigurationManager对网站配置文件web.config中的数据库连接字符串进行读取。就是这一句:
public static readonly string ConnectionStringLocalTransaction = ConfigurationManager.ConnectionStrings["SQLConnString"].ConnectionString;
所以我们的web.config中应该具有name属性为”SQLConnString”的数据库连接字符串的XML结点。
打开web.config,找到<connectionStrings/>,并将其修改为:
<connectionStrings>
<add name="SQLConnString" connectionString="Data Source=.;Initial Catalog=StudentMIS;Integrated Security=True"/>
</connectionStrings>
意思就是添加一个名为”SQLConnString”的数据库连接字符串。
这样,我们就可以通过对web.config的访问来获取需要的数据库连接字符串啦。
需要在DBUtility中添加对Configuration的引用,右击DBUtility下的引用->添加引用,选择.NET中的System.Configuation如图:
然后点击确定按钮。这样就可以在SQLHelper中对Configuration进行调用啦。
接下来我们对DAL中的User.cs进行修改。原先的复杂的数据库操作全部通过调用DBUtility中的方法来实现。
首先当然是对DAL添加DBUtility的引用。这个同志们都很熟了吧。
修改后的User.cs如下:
using System;
using System.Data;
using System.Data.SqlClient;
using DBUtility;
namespace DAL
{
public class User
{
private const string SQL_SELECT_PASSWORD_BY_USERID = "SELECT password FROM [User] WHERE UserId = @UserId";
private const string SQL_SELECT_USERNAME_BY_USERID = "SELECT UserName FROM [User] WHERE UserId = @UserId";
private const string PARM_USERID = "@UserId";
/// <summary>
/// 根据用户Id获取用户密码
/// </summary>
/// <param name="userId">用户Id</param>
/// <returns>用户密码</returns>
public string GetUserPassword(string userId)
{
SqlParameter parm = new SqlParameter(PARM_USERID, SqlDbType.VarChar, 10);//参数UserId
parm.Value = userId;//给参数赋值
Object obj = SqlHelper.ExecuteScalar(SqlHelper.ConnectionStringLocalTransaction, CommandType.Text, SQL_SELECT_PASSWORD_BY_USERID, parm);
if (obj != null) return obj.ToString();//若查询结果不为空,则将其转换为字符串
else return null;//否则返回null
}
/// <summary>
/// 根据用户Id获取用户姓名
/// </summary>
/// <param name="userId">用户Id</param>
/// <returns>用户姓名</returns>
public string GetUserName(string userId)
{
SqlParameter parm = new SqlParameter(PARM_USERID, SqlDbType.VarChar, 10);//参数UserId
parm.Value = userId;//给参数赋值
Object obj = SqlHelper.ExecuteScalar(SqlHelper.ConnectionStringLocalTransaction, CommandType.Text, SQL_SELECT_USERNAME_BY_USERID, parm);
if (obj != null) return obj.ToString();//若查询结果不为空,则将其转换为字符串
else return null;//否则返回null
}
}
}
是不是感觉清爽了很多呢。修改起来也方便。
但是有个弊端。GetUserPassword方法返回的是一个string。如果写数据库处理的哥哥一个不小心,把对数据库的操作的T-SQL写错了,这个错误仍然能返回一个string。这样,那个哥哥找错误就比较痛苦了。因为代码没有语法错误。编译执行都能通过,就是效果出了问题。
为此,得对DAL中函数的返回值进行规范。若出现了错误,编译就无法通过。这样那个哥哥就爽多了。
具体怎么做请看下一篇博文:ASP.NET三层结构演化构建之四——你不是我,我不是他