下面的方法是我在实际开发中摸索出来的,可以在很大程度上简化调用存储过程的代码。
首先来看一下C#调用存储过程的一般过程:
1、打开数据库连接SqlConnection;
2、生成一个SqlCommand;
3、向命令对象填充参数;
4、执行存储过程;
5、关闭连接;
6、其他操作。
我这里讲的主要是简化第3步操作,最终在调用存储过程的时候只需要传递 存储过程的名字 和 相应的参数值 。调用示例如下:
dbAccess.run("p_am_deleteFile", new object[]{LoginId, Request.UserHostAddress, fileId});
由于在填充参数的时候必须要两个值,一个是参数的名字,一个是参数的值。参数值是由外部传入的,不用考虑;而参数名称是和存储过程相关的东西,应该可以由存储过程名称来确定而不用每次调用的时候写上一遍。对于这个问题,如果能将存储过程的参数保存到一个全局的地方,那么在调用存储过程的时候只要能根据存储过程的名字去索引就可以了。具体实现的时候我是将这些信息保存在数据库访问组件里面,采用名字/值对的方式。代码如下:
public class InfoTable : NameObjectCollectionBase
{
public object this [ string key]
{
get
{
return ( this .BaseGet(key));
}
set
{
this .BaseSet(key, value );
}
}
}
。。。。。。
protected static InfoTable procInfoTable = new InfoTable();
。。。。。。
public static InfoTable ProcInfoTable
{
get
{
return procInfoTable;
}
}
这样的话,在实际调用存储过程的时候就只需要去查这张表就可以知道存储过程的参数名了。实现代码如下:
public DataTable run( string procName, object [] parms, ref int retValue)
{
** string [] paramInfo = ( string [])(procInfoTable[procName]);
** if (paramInfo == null )
{
ErrorInfo.setErrorInfo("未取得" + procName + "的参数!");
return null ;
}
bool bOpened = (dbConn.State == ConnectionState.Open);
if (!bOpened && !connect())
{
return null ;
}
DataSet ds = new DataSet();
try
{
SqlCommand cmd = new SqlCommand(procName, dbConn);
cmd.CommandType = CommandType.StoredProcedure;
for ( int i = 0; i < parms.Length && i < paramInfo.Length; ++i)
{
cmd.Parameters.Add( new SqlParameter(paramInfo[i], parms[i]));
}
SqlParameter parmsr = new SqlParameter( "return" , SqlDbType.Int);
parmsr.Direction = ParameterDirection.ReturnValue;
cmd.Parameters.Add(parmsr);
SqlDataAdapter adp = new SqlDataAdapter(cmd);
adp.Fill(ds);
retValue = ( int )(cmd.Parameters["return"].Value);
}
catch (Exception ex)
{
ErrorInfo.setErrorInfo(ex.Message);
retValue = -1;
}
if (!bOpened)
close();
if (ds.Tables.Count > 0)
return ds.Tables[0];
else
return null ;
}
可以看出,每个存储过程的参数列表存储为了一个 ** string [] ** 。接下来的工作就是将系统里头许许多多的存储过程的参数填充到表ProcInfoTable中。我所用的数据库是Sql Server 2000,下面给出一个存储过程来解决这个烦人的问题:
create PROCEDURE dbo.p_am_procInfo
(
@procName t_str64 --存储过程的名字
)
AS
begin
set nocount on
if @procName = '' begin
select name as procName
from sysobjects
where substring ( sysobjects . name , 1, 5) = 'p_am_'
end
else begin
select
syscolumns . name as paramName
from sysobjects , syscolumns
where sysobjects .id = syscolumns .id
and sysobjects .name = @procName
order by colid
end
end
这个存储过程有两个作用,在没有传递存储过程的名字的时候,该存储过程返回所有以”p_am_”开头的存储过程的名字;在传入了相应的存储过程名字后,该存储过程返回该存储过程的参数列表。这样一来,我们在程序开始的地方就可以将系统里的存储过程参数列表取出来并保存到数据库访问组件的ProcInfoTable属性中了。具体代码如下:
span.DBAccess dbAccess = new span.DBAccess();
//
//构造取存储过程的参数表
//
span.DBAccess.ProcInfoTable["p_am_procInfo"] = new string []{"@procName"};
//
//取得其他存储过程列表
//
DataTable dt = dbAccess.run("p_am_procInfo", new object[]{""});
if (dt == null || dt.Rows.Count <= 0)
{
return ;
}
//
//取得其他存储过程的参数表
//
foreach (DataRow dr in dt.Rows)
{
DataTable dtParams = dbAccess.run("p_am_procInfo", new object[]{dr["procName"]});
if (dtParams != null )
{
string [] paramInfo = new string [dtParams.Rows.Count];
for ( int i = 0; i < dtParams.Rows.Count; ++i)
paramInfo[i] = dtParams.Rows[i]["paramName"].ToString();
span.DBAccess.ProcInfoTable[dr["procName"].ToString()] = paramInfo;
}
}
至此,全部技术细节介绍完毕。另外,数据库访问对象的几个接口函数也一并给出:
//打开、关闭数据库连接
public bool connect( string strConn)
public bool connect()
public bool close()
//执行SQL命令(只有一个int返回)
public int exec( string procName, object [] parms)
public int exec( string sql)
//运行(返回一个DataTable)
public DataTable run( string procName, object [] parms, ref int retValue)
public DataTable run( string procName, object [] parms)
public DataTable run( string sql)
//分页查询(页号从1开始,返回一个DataTable)
public DataTable pageQuery
(
string selectCmd,
int pageSize,
int pageNumber
)