3

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

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

我不再得到 DBCommandExcept 错误消息,而是得到“连接字符串属性尚未初始化”。

IOW,目前有点像第 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问题;但是,然后我得到“连接字符串属性尚未初始化”

我认为上面唯一的非 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 event() 中写出它,并且它总是(直到现在)至少有几个条目,但在代码的最后几次更新中,它不是写多了……???

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

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());
    }
}
4

1 回答 1

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 语句中插入值,就像您所展示的那样。相反,尝试使用参数。

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

例如:下面,我的Serial_Number列是SqlDbType.NVarChar,大小是 50。SqlCeParameter这个列的一个是:

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

数据库表定义

由于我不知道您使用的是什么类型的数据,所以我创建了一个枚举类型来显示如何使用每种方法。如果您无法访问表的设计,最后的手段是“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))) {

CommandType默认情况下,是CommandType.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;
}

快乐编码!

于 2013-03-28T14:10:35.603 回答