0

私の WindowsCE / Compact Framework (.NET1.1) プロジェクトでは、コードで新しいテーブルを作成する必要があります。私はこのようにすることができると思いました:

if (! TableExists("table42"))
{
    CreateTable42();
}

public static bool TableExists(string tableName)
{
    try
    {
        using (SqlCeConnection sqlConn = new SqlCeConnection(@"Data Source=\my documents\Platypus.SDF"))
        {
            sqlConn.Open();
            string qryStr = "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = ?";
            SqlCeCommand cmd = new SqlCeCommand(qryStr, sqlConn);
            cmd.Parameters[0].Value = tableName;
            cmd.CommandType = CommandType.Text;
            int retCount = (int)cmd.ExecuteScalar();
            return retCount > 0;
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show("TableExists ex.Message == " + ex.Message);
        MessageBox.Show("TableExists ex.ToString() == " + ex.ToString());
        MessageBox.Show("TableExists ex.GetBaseException() == " + ex.GetBaseException());
        return false;
    }
}

...しかし、TableExists() の呼び出しは失敗します。そして私に示します:

TableExists ex.Message ==
TableExists ex.ToString() == System.Data.SqlServerCe.SqlCeException at System.Data.SqlServerCe.SqlConnection.ProcessResults(Int32 hr) at ...at Open(boolean silent) ...
TableExists ex.GetBaseException() == [same as ex.ToString() above]

「Int32時間」... ??? Hec Ramsey とは何ですか。

これらの環境で以前に説明したように、このプロジェクトをステップ実行できないため、MessageBox.Show() への呼び出しに依存しています。

関連するコードの残りの部分は、興味がある場合は次のとおりです。

public static void CreateTable42()
{
    try
    {
        using (SqlCeConnection con = new SqlCeConnection(@"Data Source=\my documents\Platypus.SDF"))
        {
            con.Open();
            using (SqlCeCommand com =  new SqlCeCommand(


"create table table42 (setting_id INT IDENTITY NOT NULL PRIMARY KEY,  setting_name varchar(40) not null, setting_value(63) varchar not null)", con))
                {
                    com.ExecuteNonQuery();
                    WriteSettingsVal("table42settingname","table42settingval");
                }
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show("CreateTable42 " + ex.Message);
        }
    }

public static void WriteSettingsVal(string settingName, string settingVal)
{
    using (SqlCeConnection sqlConn = new SqlCeConnection(@"Data Source=\my documents\Platypus.SDF"))
    {
        sqlConn.Open();
        string dmlStr = "insert into tabld42 (setting_name, setting_value) values(?, ?)";
        SqlCeCommand cmd = new SqlCeCommand(dmlStr, sqlConn);
        cmd.CommandType = CommandType.Text; 
        cmd.Parameters[0].Value = settingName;
        cmd.Parameters[1].Value = settingVal;
        try
        {
            cmd.ExecuteNonQuery();
        }
        catch (Exception ex)
        {
            MessageBox.Show("WriteSettingsVal " + ex.Message);
        }
    }
}

アップデート

Brad Rem のコメントへの回答:

他の作業コードは次のようになるため、param を引用符で囲む必要はないと思います。

cmd.Parameters.Add("@account_id", Dept.AccountID);

-と:

cmd.Parameters[0].Value = Dept.AccountID;

(ループ内で最初に 1 つの方法で実行し、その後は別の方法で実行します (理由は聞かないでください)。

とにかく、にやにや笑いのために、TableExists() パラメータ コードを次のように変更しました。

cmd.Parameters[0].Value = tableName;

...これに:

cmd.Parameters.Add("@TABLE_NAME", tableName);

...しかし、まったく同じ結果が得られます。

更新 2

ここで ( http://msdn.microsoft.com/en-us/library/aa237891(v=SQL.80).aspx ) 私はこれを見つけました: "Caution You must specify the SQL Server CE provider string when you open a SQL Server CEデータベース。」

彼らはこの例を挙げています:

cn.ConnectionString = "Provider=Microsoft.SQLSERVER.OLEDB.CE.2.0; data source=\Northwind.sdf"

私はそうしていません。私の接続文字列は次のとおりです。

using (SqlCeConnection sqlConn = new SqlCeConnection(@"Data Source=\my documents\CCRDB.SDF"))

それは私の問題でしょうか?

更新 3

私はこの紳士のアドバイス ( http://www.codeproject.com/Answers/629613/Why-is-my-SQLServer-CE-code-failing?cmt=487657#answer1 ) を受け、SqlCeExcpetions のキャッチを追加して、今:

public static bool TableExists(string tableName)
{
    try
    {
        using (SqlCeConnection sqlConn = new SqlCeConnection(@"Data Source=\my documents\CCRDB.SDF"))
        {
            sqlConn.Open();
            string qryStr = "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = @TABLE_NAME";
            SqlCeCommand cmd = new SqlCeCommand(qryStr, sqlConn);
            cmd.Parameters.Add("@TABLE_NAME", tableName);
            cmd.CommandType = CommandType.Text;
            int retCount = (int)cmd.ExecuteScalar();
            return retCount > 0;
        }
    }
    catch (SqlCeException sqlceex)
    {
        MessageBox.Show("TableExists sqlceex.Message == " + sqlceex.Message);
        MessageBox.Show("TableExists sqlceex.ToString() == " + sqlceex.ToString());
        return false;
        . . .

SqlCeException メッセージは次のとおりです。

更新 4

ctacke さんのサンプルコードを使ってみたのですが、どうしても Transaction が必要なのでしょうか? シナリオ/環境に合わせてコードを次のように変更する必要がありましたが、トランザクションがどうあるべきか、またはそれを構築する方法がわかりません:

public static bool TableExists(string tableName)
{
    string sql = string.Format("SELECT COUNT(*) FROM information_schema.tables WHERE table_name = '{0}'", tableName);
    try
    {
        using (SqlCeConnection sqlConn = new SqlCeConnection(@"Data Source=\my documents\HHSDB.SDF"))
        {
            SqlCeCommand command = new SqlCeCommand(sql, sqlConn);
            //command.Transaction = CurrentTransaction as SqlCeTransaction;
            command.Connection = sqlConn;
            command.CommandText = sql;
            int count = Convert.ToInt32(command.ExecuteScalar());
            return (count > 0);
        }
    }
    catch (SqlCeException sqlceex)
    {
        MessageBox.Show("TableExists sqlceex.Message == " + sqlceex.Message);
        return false;
    }
}

更新 5

このコードを使用すると、「エラー メッセージはこの例外に使用できますが、これらのメッセージはオプションであり、現在このデバイスにインストールされていないため、表示できません。インストールしてください ... NETCFv35.Messages.EN.cab "

更新 6

あまりにも典型的なことですが、このレガシーで古代のテクノロジー プロジェクトは、私に頭痛の種を与えています。一度に開くことができる接続は 1 つだけのようで、アプリは最初から 1 つを開きます。だから、私はその接続を使用する必要があります。ただし、これは SqlCeConnection ではなく DBConnection であるため、次のコードは使用できません。

using (SqlCeCommand com =  new SqlCeCommand(
       "create table hhs_settings (setting_id int identity (1,1) Primary key,  setting_name varchar(40) not null, setting_value(63) varchar not null)", frmCentral.dbconn))
{
    com.ExecuteNonQuery();
    WriteSettingsVal("beltprinter", "ZebraQL220");
}

...引数として SqlCeCommand コンストラクターに渡された既に開いている接続タイプは DBCommand であり、期待/必須の SqlCeConneection ではないためです。

このコードの触手は幅が広すぎて、根っこを引き裂いてより賢明なものにするためにリファクタリングすることはできません。ふもとの丘での一時的な一歩が、エベレストで猛烈な雪崩を引き起こします。

4

3 に答える 3

1

うわー... まだ苦労しています... 私が最初にハンドヘルド デバイス SQL-CE を使い始めたときもそうでした。私の現在のプロジェクトは C#.Net 3.5 で実行されていますが、あなたが実行している原則は同じだと思います。これが私のシステムで機能しているもので、あなたのシステムと非常によく似ています。

まず、ハンドヘルドへの接続文字列。それはただです

string myConnString  = @"Data Source=\MyFolder\MyData.sdf";

SQLドライバーへの参照なし

次に、TableExists

SqlCeCommand oCmd = new SqlCeCommand( "select * from INFORMATION_SCHEME.TABLES "
   + " where TABLE_NAME = @pTableName" );
oCmd.Parameters.Add( new SqlCeParameter( "pTableName", YourTableParameterToFunction ));

「@pTableName」は、「TABLE_NAME」列を区別し、あいまいさに関する問題を完全に防ぐためのものです。パラメータは余分な「@」を取得しません。SQL では、@ は変数を探すことを示します... "pTableName" の SqlCeParameter は、SQL コマンドと同じである必要があります (ただし、先頭の "@" は除きます)。

ExecuteScalar への呼び出しを発行する代わりに、実際にデータを DataTable にプルしています。

DataTable oTmpTbl = new DataTable();
SqlCeDataAdapter da = new SqlCeDataAdapter( oCmd );
da.Fill( oTmpTbl );
bool tblExists = oTbl.Rows.Count > 0;

このようにして、レコードが返されるか返されないかのどちらかです...もし返された場合、レコードの数は> 0になるはずです。「LIKE」を実行していないため、問題のレコードのみを返す必要があります。

挿入、更新、および削除を行うとき、私は常にパラメーターの前に「@pWhateverColumn」などのプレフィックスを付け、SqlCeParameter が同じ名前で「@」がないことを確認してきました。私は何の問題も抱えておらず、このプロジェクトは何年も実行されています。はい、これは .net 3.5 アプリですが、接続とクエリの基本は同じである必要があります。

それがすべてアプリケーション内にある場合は、単一のグローバル静的「接続」オブジェクトを作成するようなことを試みます。次に、それを処理する単一の静的メソッド。次に、「使用」試行ごとに新しい接続を行う代わりに、次のように変更します...

public static class ConnectionHandler
{
   static SqlCeConnection myGlobalConnection;

   public static SqlCeConnection GetConnection()
   {
      if( myGlobalConnection == null )
         myGlobalConnection = new SqlCeConnection();

      return myGlobalConnection;
   }

   public static bool SqlConnect()
   {
      GetConnection();   // just to ensure object is created

      if( myGlobalConnection.State != System.Data.ConnectionState.Open)
      {
         try
         {
            myGlobalConnection.ConnectionString = @"Data Source=\MyFolder\MyDatabase.sdf";
            myGlobalConnection.Open();
         }
         catch( Exception ex)
         {
            // optionally messagebox, or preserve the connection error to the user
         }
      }

      if( myGlobalConnection.State != System.Data.ConnectionState.Open )
         MessageBox.Show( "notify user");

      // return if it IS successful at opening the connection (or was already open)
      return myGlobalConnection.State == System.Data.ConnectionState.Open;
   }

   public static void SqlDisconnect()
   {
      if (myGlobalConnection!= null)
      {
         if (myGlobalConnection.State == ConnectionState.Open)
            myGlobalConnection.Close();

         // In case some "other" state, always try to force CLOSE
         // such as Connecting, Broken, Fetching, etc...
         try
         { myGlobalConnection.Close(); }
         catch
         { // notify user if issue}
      }
   }
}

...他のクラス/関数で...

   if( ConnectionHandler.SqlConnect() )
      Using( SqlCeConnection conn = ConnectionHandler.GetConnection )
      {
         // do your stuff
      }

...最後に、アプリが完成したとき、または必要なときに...

ConnectionHandler.SqlDisconnect();

これにより、物事が一元化され、オープン/クローズ、接続文字列があちこちに埋め込まれていることなどを心配する必要がなくなります...接続できない場合は、クエリを実行できません。そこまで到達できない場合は、クエリを実行しようとしないでください。

于 2013-08-01T17:07:59.607 に答える
0

INFORMATION_SCHEMA システム ビューの権限の問題である可能性があります。以下を試してください。

GRANT VIEW DEFINITION TO your_user;

詳しくはこちら

于 2013-07-31T23:45:11.393 に答える