2

SQL Server と並行して C# の学習の初期段階を無事に終えた後、私が使用したさまざまなチュートリアルでは、 global やさらには変数を宣言することで、単に間違っていることに気付きSqlConnectionましSqlDataAdapterDataSet

結果として、このコードはシングル スレッド アプリケーションではうまく機能しますが、マルチスレッド環境ではそれほどうまく機能しません。ソリューションの調査で、MSDNとこの教育的な回答の両方が、SQL トランザクションの「アトミック」部分を using/try メソッドでラップすることを推奨していることを発見しました。

private static void CreateCommand(string queryString, string connectionString)
{
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        try
        {
            SqlCommand command = new SqlCommand(queryString, connection);
            command.Connection.Open();
            command.ExecuteNonQuery();
        }
        catch (InvalidOperationException)
        {
            //log and/or rethrow or ignore
        }
        catch (SqlException)
        {
            //log and/or rethrow or ignore
        }
        catch (ArgumentException)
        {
            //log and/or rethrow or ignore
        }
    }
}

そこで、これからやろうとしていることは、コード全体をこのようなラッパーを使用するように変換することです。しかし、これを進める前に、このアプローチのトレードオフを理解したいと思います。私の経験では、通常、大規模な設計者/エンジニアのチームが特定の防御機能を含めないことを決定するのには十分な理由があります。これは、C/C++ プログラマーとしての私の観点からすると、C# の全体的な価値命題が "防御力" である場合に特に興味深いものです (トレードオフはよく知られている CLR パフォーマンス ヒットです)。

私の質問を要約するには:

  1. 上記のように、コード内のすべてのトランザクションをカプセル化することのトレードオフは何ですか?
  2. 探すべき注意事項はありますか?
4

4 に答える 4

4

その理由は柔軟性にあります。開発者はコマンドをトランザクションに含めたいですか? 特定のエラーで再試行したいですか? もしそうなら何回ですか? スレッドプールからの接続が必要ですか? または毎回新しい接続を作成しますか? (パフォーマンスのオーバーヘッドを伴う) )、SQL 接続が必要か、それともより一般的な DbConnection が必要かなど。

ただし、MS はエンタープライズ ライブラリを提供しています。これは、オープン ソース ライブラリ内の物事に対する多くの一般的なアプローチをまとめた一連の機能です。データ アクセス ブロックを見てみましょう: http://msdn.microsoft.com/en-us/library/ff632023.aspx

于 2012-11-24T12:38:38.687 に答える
3

次の理由により、そのようなメソッドは組み込まれていません。

  • コマンドごとにデータベースを接続および切断するのは経済的ではありません。コード内の特定のポイントで複数のコマンドを実行する場合は、接続を繰り返し開いたり閉じたりするのではなく、同じ接続を使用する必要があります。

  • メソッドは、各種類の例外に対して何をしたいのかを知ることができないため、できることはそれらを再スローすることだけであり、最初に例外をキャッチしても意味がありません。

したがって、メソッドが行うほとんどすべてのことは、それぞれの状況に固有です。

その上、メソッドは一般的に有用であるためにもっと多くのことをしなければなりません. コマンドタイプとパラメーターのパラメーターを取得する必要があります。それ以外の場合は、テキスト クエリにしか使用できず、ストアド プロシージャやパラメーター化されたクエリを使用する代わりに SQL クエリを動的に作成することを奨励し、一般的なライブラリがやりたいことではありません。

于 2012-11-24T12:33:32.993 に答える
2

1-実際のトレードオフはありません。かなり標準的です。

2-あなたのコードはSQLクエリとして実行される文字列としてコマンドを送信することは問題ありませんが、それは少しの柔軟性を欠いています:

  • command.Parameters.AddWithValue(...)ストアドプロシージャの使用を開始すると必須となるパラメータ化されたクエリ()は使用できません
  • outputこのようなパラメータは使用できません
  • 照会されたものは何でもできません

私は次のようなものを使用することを好みます:

private static void CallProc(string storedProcName, Action<SqlCommand> fillParams, Action postAction, Action onError)
{
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        using (SqlCommand command = new SqlCommand(String.Format("[dbo].[{0}]", storedProcName), connection))
        {
            try 
            {
                if(fillParams != null)
                    fillParams(command);
                command.Connection.Open();
                command.ExecuteNonQuery();
                if(postAction != null)
                    postAction();
             }        
            catch (InvalidOperationException)
            {
                //log and/or rethrow or ignore
                throw;
            }
            catch (SqlException)
            {
                //log and/or rethrow or ignore
                throw;
            }
            catch (ArgumentException)
            {
                //log and/or rethrow or ignore
                throw;
            }
            catch
            {
                if(onError != null)
                   onError();
            }
        }
    }
}

次に、戻り値、出力パラメータなどを処理するためのバリアントを作成できます。

そして、あなたは次のように呼びます:

CallProc("myStoredProc",
    command => 
    {
        command.Parameters.AddWithValue("@paramNameOne", "its value here");
        // More parameters for the stored proc...
    },
    null,
    null);
于 2012-11-24T12:43:51.830 に答える
1

投稿した静的メソッドのような「ボトルネック」メソッドに機能をカプセル化して、すべてのデータベースアクセスが1つの変更しやすい共有コードに実装される限り、多くの場合、トレードオフは必要ありません。膨大な量のコードを書き直すことなく、後で実装を変更できるためです。

毎回新しい接続を作成することにより、接続の開閉ごとに高額なオーバーヘッドが発生する可能性があるというリスクがあります。ただし、接続をプールする必要があります。その場合、オーバーヘッドはそれほど大きくなく、このパフォーマンスへの影響は最小限に抑えられます。

もう1つのアプローチは、単一の接続を作成して開いたままにし、すべてのクエリで共有することです。トランザクションあたりのオーバーヘッドを最小限に抑えるため、これは間違いなくより効率的です。ただし、パフォーマンスの向上は最小限である可能性があります。

どちらの場合も、すべてのデータベースクエリが単一のスレッドで動作することを確認しない限り、解決すべき追加のスレッド(複数の同時クエリ)の問題が発生します。パフォーマンスへの影響はすべて、1秒間に実行するクエリの数によって異なります。もちろん、非常に非効率的なクエリを使用している場合は、接続アプローチがどれほど効率的であるかは関係ありません。「最適化」の時間を最悪のパフォーマンスの問題に集中させる必要があります。

したがって、今のところシンプルに保ち、時期尚早の最適化を回避することをお勧めしますが、データベースアクセスコードの実装を別のレイヤーに維持して、メインコードベースがアクセスレイヤーにコマンドを発行するだけで、データベース固有のものが最小限になるようにしてくださいその中のコード。データベースについて「知っている」ことが少なければ少ないほどよい。これにより、基盤となる実装を変更したり、コードを移植して将来別のデータベースエンジンを使用したりすることがはるかに簡単になります。

これに役立つ別のアプローチは、クエリをストアドプロシージャにカプセル化することです。これは、プログラムがプロシージャの名前とそのパラメータを知っているが、アクセスされる実際のテーブル/列はデータベース内に隠されていることを意味します。その場合、コードはデータベースの低レベルの構造をできるだけ知らないため、柔軟性、保守性、および移植性が向上します。ストアドプロシージャの呼び出しは、一般的なSQLコマンドを送信するよりも効率的です。

于 2012-11-24T12:44:22.780 に答える