.Text中的Bug

  今天下午三点左右,打开博客园的文章时,出现了这样的错误:“The SqlParameter with ParameterName '@EntryID' is already contained by another SqlParameterCollection”。我重启了IIS,临时消除了故障。
  我仔细查看了.Text中的代码,发现了两处问题:
1、打开一篇文章时,.Text中调用的是blog_GetEntryByID存储过程,而在创建存储过程的参数时,用的是这样的代码:
SqlParameter[] p =
    {
     SqlHelper.MakeInParam("@EntryID",SqlDbType.Int,0,EntryID)
    };
一般情况下, 0应该改成4,一开始怀疑是这个原因造成上述故障的。但查了一下MSDN, 在SqlParameter的帮助中有这样的一句话:“如果未在 size 参数中显式设置 Size,则可根据 dbType 参数的值推断出该大小。”。即使设成0, 系统也会自动推断出SqlDbType.Int的大小。实际上,如果上面的代码有问题,.Text根本就无法正常使用。所以这个问题不是一个问题。
2、第二问题是在google上查找到的,我找到了这样的文章:http://www.dotnetforums.com/showthread.php?t=244。文章中有这样的代码:
try {
return cmd.ExecuteReader(CommandBehavior.CloseConnection);

}catch(SqlException sqle){
//if stp FAILS and we don't clear command collection,
//the next request will throw: "The SqlParameter with ParameterName
//'XXXX' is already contained by another SqlParameterCollection" error

cmd.Parameters.Clear();

cmd.Dispose();
if (conn.State == ConnectionState.Open) {
conn.Close();
}
代码的注释中讲得很明白,如果在cmd.ExecuteReader时产生异常,而没有调用cmd.Parameters.Clear();就会发生博客园今天出现的故障。
再看一下,.Text中的相关代码:

private static SqlDataReader ExecuteReader(SqlConnection connection, SqlTransaction transaction, CommandType commandType, string commandText, SqlParameter[] commandParameters, SqlConnectionOwnership connectionOwnership)

         {   

             

              //create a command and prepare it for execution

              SqlCommand cmd = new SqlCommand();

              PrepareCommand(cmd, connection, transaction, commandType, commandText, commandParameters);

             

              //create a reader

              SqlDataReader dr;

 

              // call ExecuteReader with the appropriate CommandBehavior

              if (connectionOwnership == SqlConnectionOwnership.External)

              {

                   dr = cmd.ExecuteReader();

              }

              else

              {

                   dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);

              }

             

              // detach the SqlParameters from the command object, so they can be used again.

              cmd.Parameters.Clear();

             

              return dr;

         }

其中的cmd.Parameters.Clear();表明作者已经考虑到了这个问题,但却忘了考虑异常情况, 应该是在finally中执行cmd.Parameters.Clear(); 正确的代码如下:

private static SqlDataReader ExecuteReader(SqlConnection connection, SqlTransaction transaction, CommandType commandType, string commandText, SqlParameter[] commandParameters, SqlConnectionOwnership connectionOwnership)

         {   

              //create a command and prepare it for execution

              SqlCommand cmd = new SqlCommand();

              //create a reader

              SqlDataReader dr;

              try

              {

                   PrepareCommand(cmd, connection, transaction, commandType, commandText, commandParameters);

             

                   // call ExecuteReader with the appropriate CommandBehavior

                   if (connectionOwnership == SqlConnectionOwnership.External)

                   {

                       dr = cmd.ExecuteReader();

                   }

                   else

                   {

                       dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);

                   }

              }

              finally

              {

                   // detach the SqlParameters from the command object, so they can be used again.

                   cmd.Parameters.Clear();

              }

              return dr;

         }

  
BTW:
  如何更好地捕捉.Text产生的异常?我觉得仅仅是在产生异常时抛出异常并在Web页面显示是不够的。因为管理员无法知道其他人使用时产生的异常。所以在Web页面显示异常的同时,应该把异常写入日志。在.Text中,有一个方便、简单的方法,就是在Error.aspx.cs中加上Dottext.Framework.Logger.LogManager.Log("Error",exceptionMsgs.ToString());

posted @ 2004-06-26 17:42  dudu  阅读(3152)  评论(18编辑  收藏  举报