为什么处理SqlCeConnection既解决了一个异常又引发了另一个异常呢?

时间:2013-03-27 17:46:05

标签: c# compact-framework sql-server-ce windows-ce

在应用会话中两次调用相同的查询方法时,我得到" DBCommandExcept"

作为一个实验,我决定在方法结束时处理连接对象,看看是不是问题。

我不再获取DBCommandExcept错误消息,而是获取," connectionstring属性尚未初始化。"

IOW,它现在是一种Catch-22的情况。相关代码是:

string query = "SELECT Bla FROM Blah";
SqlCeCommand cmd = new SqlCeCommand(query);
cmd.CommandType = CommandType.Text;
SqlCeConnection conn = dbconn.GetConnection(); 
cmd.CommandType = CommandType.Text;//probably unnecessary
cmd.Connection = conn; 

SqlCeDataReader myReader = cmd.ExecuteReader(CommandBehavior.SingleRow);
try
{
    if (myReader.Read())
    {
        itemID = myReader.GetString(ITEMID_INDEX);
        packSize = myReader.GetString(PACKSIZE_INDEX);
        recordFound = true;
    }
}
catch (Exception ex)
{
    RRDR.LogMsgs.Append(string.Format("Exception in PopulateControlsIfVendorItemsFound(): {0}", ex.Message));
}
finally
{
    myReader.Close();
    //if (null != conn)
    //{
    //  conn.Dispose();
    //}
}

// Re:上面的注释掉的块:当它处于活动状态时,看不到DBCommandExcept问题;然而,我接着," connectionstring属性尚未初始化"

我认为上面唯一的非SQL-CE标准位是dbConn.GetConnection()。这里有一些代码:

SqlCeConnection objCon = null; 

. . .

public SqlCeConnection GetConnection()
{
    return objCon;
}


private DBConnection() // class constructor
{
    try
    {
        . . .
        objCon = new SqlCeConnection(conStr);
        objCon.Open();
        . . .

同样,在应用程序的一次运行期间,错误(任何一个,无论选择哪个"选择")都只能通过此方法第二次看到。第一次工作正常。

更新

我添加了下面的代码,评论说明了悲惨的故事:

// With conn check only, still get two consecutive DBCommandExcepts
// With cmd check only, still get two consecutive DBCommandExcepts
// With both, still get two consecutive DBCommandExcepts; IOW, all have the same effect
if (null != conn)
{
    conn.Close();
}
if (null != cmd)
{
    cmd.Dispose();
}

更新2

根据unicron的建议,我尝试使用"使用。"

在三种情况中的两种情况下(SqlCeCommand和SqlCeDataReader),转换为"使用"没有差异;在另一个(SqlCeConnection)中,它引发了错误的消息," ConnectionString属性尚未初始化。"

尽管如此,使用这两个代码时代码更清晰,所以感谢你在最佳实践方向上的推动。

现在看来是这样的:

private bool PopulateControlsIfPlatypusItemsFound()
{
    const int ITEMID_INDEX = 0;
    const int PACKSIZE_INDEX = 1;
    bool recordFound = false;

    try
    {
        string PlatypusId = txtPlatypus.Text.ToString().Trim();
        string PlatypusItemId = txtUPC.Text.ToString().Trim();
        string itemID = string.Empty;
        string packSize = string.Empty;

        string query = string.Format("SELECT ItemID, PackSize FROM PlatypusItems WHERE PlatypusID = {0} AND PlatypusItemID = {1}", PlatypusId, PlatypusItemId);
        using (SqlCeCommand cmd = new SqlCeCommand(query))
        {
            cmd.CommandType = CommandType.Text;
            SqlCeConnection conn = dbconn.GetConnection(); 
            if ((null != conn) && (!conn.State.Equals(ConnectionState.Open)))
            {
                conn.Open();
                TTBT.LogMsgs.Append("Connection opened");
            }
            cmd.CommandType = CommandType.Text;//probably unnecessary
            cmd.Connection = conn;

            using (SqlCeDataReader myReader = cmd.ExecuteReader(CommandBehavior.SingleRow))
            {
                if (myReader.Read())
                {
                    itemID = myReader.GetString(ITEMID_INDEX);
                    packSize = myReader.GetString(PACKSIZE_INDEX);
                    recordFound = true;
                }
            }

            txtID.Text = itemID;
            txtSize.Text = packSize;
            return recordFound;
        }
    }
    catch (Exception ex)
    {
        TTBT.LogMsgs.Append(string.Format("Exception in PopulateControlsIfPlatypusItemsFound: {0} - {1}\r\n", ex.Message, ex.InnerException));
        return recordFound;
    }
}

更新3

我通过使用通用排序替换自定义连接代码,添加另一个"使用"更接近常态。混合:

private bool PopulateControlsIfVendorItemsFound()
{
    const int ITEMID_INDEX = 0;
    const int PACKSIZE_INDEX = 1;
    bool recordFound = false;

    DUCKBILL.LogMsgs.Append("Made it into frmEntry.PopulateControlsIfVendorItemsFound()\r\n");

    try
    {
        string vendorId = txtVendor.Text.ToString().Trim();
        string vendorItemId = txtUPC.Text.ToString().Trim();
        string itemID = string.Empty;
        string packSize = string.Empty;

        if ( dbconn.isValidTable( "VendorItems" ) == -1 )
        {
            DUCKBILL.LogMsgs.Append("VendorItems not a valid table");//do not see this msg; good! VendorItems is seen as valid...
            return false;
        }

        string query = string.Format("SELECT ItemID, PackSize FROM VendorItems WHERE VendorID = {0} AND VendorItemID = {1}", vendorId, vendorItemId);
    using (SqlCeCommand cmd = new SqlCeCommand(query))
    {
        cmd.CommandType = CommandType.Text;
        using (SqlCeConnection conn = new SqlCeConnection())
        {
            string filename = "\\badPlace2B\\CCRDB.SDF";
            conn.ConnectionString = string.Format("Data Source = {0}", filename);
            cmd.CommandType = CommandType.Text;//probably unnecessary/moot
            cmd.Connection = conn; 
            conn.Open();

            using (SqlCeDataReader myReader = cmd.ExecuteReader(CommandBehavior.SingleRow))
            {
                if (myReader.Read())
                {
                    itemID = myReader.GetString(ITEMID_INDEX);
                    packSize = myReader.GetString(PACKSIZE_INDEX);
                    recordFound = true;
                }
            }
        }

        txtID.Text = itemID;
        txtSize.Text = packSize;
        return recordFound;
    }
    }
    catch (Exception ex)
    {
        DUCKBILL.LogMsgs.Append(string.Format("Exception in PopulateControlsIfVendorItemsFound: {0} - {1}\r\n", ex.Message, ex.InnerException));
        return recordFound;
    }
}

...但我仍然得到" DBCommandExcept" ...

至于"打开连接时停止前进,"是不是有必要这样做?上面的代码怎么可能/应该不同?

更新4

更奇怪的是,现在我的调试日志文件已停止写入。我一直在全局异常处理程序和主窗体的Closed事件()中写出来,并且它总是(至今)至少有几个条目,但在代码的最后几次更新中,它不再被写了...... ????

两个地方全局异常处理程序和主窗体的Closed事件(),代码是这样的:

public static bool inDebugMode = true;

. . .
if (CCR.inDebugMode)
{
    DateTime dt = DateTime.Now;
    string timeAsStr = string.Format("{0}_{1}_{2}_{3}.txt", dt.Hour, dt.Minute, dt.Second, dt.Millisecond);
    using (StreamWriter file = new StreamWriter(timeAsStr))
    {
        // If the app closes normally, this is how the file is written; if it doesn't, 
        // (it crashed) it's written in PDAClient.ExceptionHandler()
        file.WriteLine(SSCS.LogMsgs.ToString());
    }
}

1 个答案:

答案 0 :(得分:2)

由于您正在对数据库文件进行多次调用(不会发生变化),因此我首先将您的连接字符串和SQL语句定义在类的顶部作为全局值:

private const int ITEMID_INDEX = 0;
private const int PACKSIZE_INDEX = 1;
private const string SQL_CONN_STR = "Data Source=\\badPlace2B\\CCRDB.SDF";
private const string SQL_GET_VENDOR_ITEMS = "SELECT ItemID, PackSize " + 
  "FROM VendorItems " +
  "WHERE VendorID=@VendorID AND VendorItemID=@VendorItemID";

这些永远不会改变,因此每次调用例行程序时都没有理由再次定义它们。

就个人而言,我不喜欢在SQL语句中插入值,就像您所示。相反,尝试使用参数。

要使用参数,您需要查看数据库以查看列VendorIDVendorItemID的列类型。我的猜测是它们都是 int 值,但这些值可能是 GUID ,需要VarChar类型字符串。如果这些是字符串,则应记下列定义的大小。

例如:下面,我的Serial_Number列为SqlDbType.NVarChar,大小为50.此列的SqlCeParameter为:

cmd.Parameters.Add("@Serial_Number", SqlDbType.NVarChar, 50).Value = txtSerial_Number.Text.Trim();

db table definition

由于我不知道您使用的是哪种类型的数据,因此我创建了一个枚举类型来显示每种方法的使用方式。如果您无法访问该表的设计,最后的手段是“AddWithValue”(我个人讨厌那个,因为它让我看起来像我不知道我的数据库里面有什么)。

enum ParamStyle { AddWithValue, AddIntegers, AddVarChar }

要使用此枚举类型,我修改了方法的签名以传递该值:

private bool PopulateControlsIfVendorItemsFound(ParamStyle style) {

显然,你不需要这个,因为你应该知道你要编写什么技术。

我无法弄清楚你的dbconn对象是什么。最初,我认为这是你的SqlCeConnection,但是没有isValidTable方法,所以我只是评论出来了:

  //if (dbconn.isValidTable("VendorItems") == -1) {
  //  DUCKBILL.LogMsgs.Append("VendorItems not a valid table");//do not see this msg; good! VendorItems is seen as valid...
  //  return false;
  //}

说到SqlCeConnection ......

我将您的SqlCeCommand实例与您的SqlCeConnection实例合并。更少的代码通常意味着更少的错误:

  using (var cmd = new SqlCeCommand(SQL_GET_VENDOR_ITEMS, new SqlCeConnection(SQL_CONN_STR))) {

默认情况下,CommandTypeCommandType.Text,因此不需要此行:

    // cmd.CommandType = CommandType.Text; (this is the default)

我将大部分变量读取移到try/catch例程之外,因为这些都不会导致生成异常。

此外,我使用了更具针对性的SqlCeException而非普通Exception。块中唯一可能失败的是SqlCe相关的内容,SqlCeException将提供比一般Exception对象更好/更具体的错误消息。

    } catch (SqlCeException err) {

那么,它们看起来是什么样的?

代码:

enum ParamStyle { AddWithValue, AddIntegers, AddVarChar }
private const int ITEMID_INDEX = 0;
private const int PACKSIZE_INDEX = 1;
private const string SQL_CONN_STR = "Data Source=\\badPlace2B\\CCRDB.SDF";
private const string SQL_GET_VENDOR_ITEMS = "SELECT ItemID, PackSize FROM VendorItems WHERE VendorID=@VendorID AND VendorItemID=@VendorItemID";

private bool PopulateControlsIfVendorItemsFound(ParamStyle style) {
  bool recordFound = false;

  //DUCKBILL.LogMsgs.Append("Made it into frmEntry.PopulateControlsIfVendorItemsFound()\r\n");
  string itemID = null;
  string packSize = null;
  //string vendorId = txtVendor.Text.Trim();
  //string vendorItemId = txtUPC.Text.Trim();
  //string query = string.Format("SELECT ItemID, PackSize FROM VendorItems WHERE VendorID = {0} AND VendorItemID = {1}", vendorId, vendorItemId);

  //if (dbconn.isValidTable("VendorItems") == -1) {
  //  DUCKBILL.LogMsgs.Append("VendorItems not a valid table");//do not see this msg; good! VendorItems is seen as valid...
  //  return false;
  //}
  using (var cmd = new SqlCeCommand(SQL_GET_VENDOR_ITEMS, new SqlCeConnection(SQL_CONN_STR))) {
    // cmd.CommandType = CommandType.Text; (this is the default)
    if (style == ParamStyle.AddIntegers) { // Adding Integers:
      cmd.Parameters.Add("@VendorID", SqlDbType.Int).Value = Convert.ToInt32(txtVendor.Text.Trim());
      cmd.Parameters.Add("@VendorItemID", SqlDbType.Int).Value = Convert.ToInt32(txtUPC.Text.Trim());
    } else if (style == ParamStyle.AddVarChar) { // Adding String Values
      // NOTE: Here, you should look in your database table and
      // use the size you defined for your VendorID and VendorItemID columns.
      cmd.Parameters.Add("@VendorID", SqlDbType.VarChar, 25).Value = txtVendor.Text.Trim();
      cmd.Parameters.Add("@VendorItemID", SqlDbType.VarChar, 50).Value = txtUPC.Text.Trim();
    } else if (style == ParamStyle.AddWithValue) { // Adding as Objects (only if you don't know what the data types are)
      cmd.Parameters.AddWithValue("@VendorID", txtVendor.Text.Trim());
      cmd.Parameters.AddWithValue("@VendorItemID", txtUPC.Text.Trim());
    }
    try {
      cmd.Connection.Open();
      using (var myReader = cmd.ExecuteReader(CommandBehavior.SingleRow)) {
        if (myReader.Read()) {
          itemID = myReader.GetString(ITEMID_INDEX);
          packSize = myReader.GetString(PACKSIZE_INDEX);
          recordFound = true;
        }
      }
    } catch (SqlCeException err) {
      //DUCKBILL.LogMsgs.Append(string.Format("Exception in PopulateControlsIfVendorItemsFound: {0}\r\n", err.Message));
      // (I never return from a 'catch' statement) return recordFound;
    } finally {
      if (cmd.Connection.State == ConnectionState.Open) {
        cmd.Connection.Close();
      }
    }
  }
  if (recordFound) { // set these last, and set them OUTSIDE of the try/catch block
    txtID.Text = itemID;
    txtSize.Text = packSize;
  }
  return recordFound;
}

快乐编码!