3

アプリのセッションで同じクエリ メソッドを 2 回呼び出すと、"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();
        . . .

繰り返しますが、エラー (私が "選択" したもの) は、アプリの 1 回の実行中にこのメソッドを介して 2 回目にのみ表示されます。初めてはうまくいきます。

アップデート

私は以下のコードを追加しました.コメントは悲惨な話を伝えています:

// 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

ユニクロンの提案に基づいて、「using」を使用してみました。

3 つのケースのうち 2 つ (SqlCeCommand と SqlCeDataReader) では、"using" に変換しても違いはありませんでした。もう 1 つ (SqlCeConnection) では、「ConnectionString プロパティが初期化されていません。」というエラー メッセージが発生しました。

それでも、コードは 2 つの使用法でよりきれいになっているので、ベスト プラクティスの方向性を示してくれてありがとう。

現在の外観は次のとおりです。

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 に答える