6

StackOverflow サイトのリンク (以下の参照) に基づいて、C# アプリケーションから MySQL データベースへのクエリを実行するためのこのコード ブロックを考え出しました。

using (var dbConn = new MySqlConnection(config.DatabaseConnection))
{
    using (var cmd = dbConn.CreateCommand())
    {
        dbConn.Open();
        cmd.CommandType = CommandType.Text;
        cmd.CommandText = "SELECT version() as Version";

        using (IDataReader reader = cmd.ExecuteReader())
        {
            if (reader.Read())
            {
                Console.WriteLine("Database Version: " + reader.GetString(reader.GetOrdinal("Version")));
            }
        }
    }
}

これに関する問題は、作成するクエリのグループがあるたびに、この大量のコード ブロックを作成する必要があることです。これは、アプリケーションの存続期間中、接続を開いたままにしない (およびすべきではない) ためです。 .

usingサポート構造 (ネストされたs、接続を開くなど) を構築し、代わりに接続文字列と実行したいクエリを渡して結果を取得するより効率的な方法はありますか?

参照された質問:

私が見たのはそのうちの3つです。他にもいくつかありましたが、私の Google-fu は現在それらを再検索できません。これらはすべて、単一のクエリを実行する方法に対する答えを提供します。個別のビジネス ロジック クエリを実行したい (そのうちのいくつかは繰り返し)、不要なコードを繰り返したくありません。

私が試したこと: nawfal からのコメントに基づいて、次の 2 つの方法があります。

private MySqlDataReader RunSqlQuery(string query)
{
    Dictionary<string, string> queryParms = new Dictionary<string, string>();
    MySqlDataReader QueryResult = RunSqlQuery(query, queryParms);
    return QueryResult;
}

private MySqlDataReader RunSqlQuery(string query, Dictionary<string, string> queryParms)
{
    MySqlDataReader reader = null;
    if (queryParms.Count > 0)
    {
        // Assign parameters
    }

    try
    {
        using (var dbConn = new MySqlConnection(config.DatabaseConnection))
        {
            using (var cmd = dbConn.CreateCommand())
            {
                dbConn.Open();
                cmd.CommandType = CommandType.Text;
                cmd.CommandText = query;

                using (reader = cmd.ExecuteReader())
                {
                    return reader;
                }
            }
        }
    }
    catch (MySqlException ex)
    {
        // Oops.
    }

    return reader;
}

この試行の問題はreader、メソッドから返されたときに が閉じることです。

4

5 に答える 5

3

オブジェクト リレーショナル マッパー (ORM)の使用を検討したことがありますか? 私自身、 Castle Active RecordNHibernateが好きですが、他にもたくさんありますEntity FrameworkLinq to SQLも、人気のある Microsoft ソリューションです。

これらのツールを使用すると、クエリは非常に単純な CRUD メソッド呼び出しになり、(ほとんどの場合) 接続とセッションの処理が行われます。

于 2012-11-10T02:37:45.183 に答える
2

Action sまたはFunc sを使用して、あなたが求めているものを取得できます。

このように呼び出されます...

RunSqlQuery("SELECT * FROM ...", reader => ReadResult(reader));

private bool ReadResult(MySqlDataReader reader)
{
    //Use the reader to read the result

    if (!success)
        return false;

    return true;
}

このように実装...

private bool RunSqlQuery(string query, Func<MySqlDataReader, bool> readerAction)
{
    Dictionary<string, string> queryParms = new Dictionary<string, string>();
    return RunSqlQuery(query, readerAction, queryParms);
}

private bool RunSqlQuery(string query, Func<MySqlDataReader, bool> readerAction, Dictionary<string, string> queryParms)
{
    MySqlDataReader reader = null;
    if (queryParms.Count > 0)
    {
        // Assign parameters
    }

    try
    {
        using (var dbConn = new MySqlConnection(config.DatabaseConnection))
        {
            using (var cmd = dbConn.CreateCommand())
            {
                dbConn.Open();
                cmd.CommandType = CommandType.Text;
                cmd.CommandText = query;

                using (reader = cmd.ExecuteReader())
                {
                    return readerAction.Invoke(reader);
                }
            }
        }
    }
    catch (MySqlException ex)
    {
        // Oops.
        return false;
    }
}
于 2012-11-12T19:03:00.613 に答える
2

RunSqlQuery メソッド内の using ステートメントでリーダーを作成する代わりに、直接返すことができます。

return cmd.ExecuteReader();

次に、RunSqlQuery への呼び出しを using ステートメントでラップします。

using( var reader = RunSqlQuery(....) ) 
{        
  // Do stuff with reader.    
}
于 2012-11-12T06:09:42.270 に答える
1

なぜメソッドからデータリーダーを返したいのですか? usingブロックの中に入れると閉じます。また、 のインスタンスを取得してからパラメータを代入できるIDbCommandので、その部分をusingブロック内に移動しました。

厳密にデータリーダーを返したい場合は、キーワードIEnumerable<IDataRecord>を使用して返すことをお勧めします。yield

private IEnumerable<IDataRecord> RunSqlQuery(string query, 
                                             Dictionary<string, string> queryParms)
{
    using (var dbConn = new MySqlConnection(config.DatabaseConnection))
    {
        using (var cmd = dbConn.CreateCommand())
        {
            if (queryParms.Count > 0)
            {
                // Assign parameters
            }
            cmd.CommandText = query;
            cmd.Connection.Open();
            using (var reader = cmd.ExecuteReader())
                foreach (IDataRecord record in reader as IEnumerable)
                    yield return record;
        }
    }
}

または、この質問のように、そこにあるデータ自体を読み取り、データを返すことをお勧めします。そうすれば、db クラス外の db 名前空間のクラスに依存する必要がなくなります。

于 2012-11-12T04:40:51.500 に答える
0

私はその道をたどってきました。ORM の提案に沿って、EF Code First をお勧めします。トピックから少し外れて申し訳ありませんが、EF Code First を使用した後、このパターンに戻ることについて考え直したことがありません。

Code First の前は EF は非常に面倒でしたが、現在は成熟しており、DB が構造を変更する可能性がある場合、つまり、新しいアプリ機能には新しいテーブルまたは列が必要な場合は、EF Code First アプローチをお勧めします。それがサードパーティのデータベースまたは別のアプリのデータベースであり、他の誰かがその構造を管理している場合、変更をデプロイするたびにデータモデルを更新するだけで済みます。その場合、Code First を使用せず、代わりに従来の EF を使用します。既存のデータベースに基づいてモデルを生成/更新します。

既存のコード ベースをそのまま維持しながら、EF を採用して使用を開始できることに注意してください。ただし、これは、フレームワークが ADO​​ オブジェクトの使用にどの程度依存しているかによって異なります。EF Power Tools 拡張機能には、Code First モデルを生成する方法があります。または、従来の非 Code First EF を使用して、データベースからモーダルを生成することもできます。

クエリを実行したい場合は、インフラストラクチャ コードやラッパーを大量に用意しなくても、クエリを実行しようとしている内容にすぐに取りかかることができます。上記のようなラッパーに関するもう 1 つの点は、RunSqlQuery ヘルパーの代わりに ADO API の使用に戻らなければならないエッジ ケースがあることです。

通常、GetActivePeopleNames のようなメソッドはありませんが、必要な場所にクエリを配置するだけなので、これは些細な例です。綿密なコードに関してはオーバーヘッドがほとんどないため、他のすべての中でクエリを使用しても邪魔になりません。ビジネス ロジックからクエリとデータ変換を抽象化するために、いくつかのプレゼンター パターンを実行しますが。

HREntities db = new HREntities();

private ICollection<string> GetActivePeopleNames()
{      
  return db.People.Where(p => p.IsActive).Select(p => p.FirstName + " " + p.LastName)
    .ToList();
}

パラメータ オブジェクトを作成する必要はありませんでした。何らかの変数を使用できたのでWhere(p => p.IsActive == someBool)、そのコンテキストでの SQL インジェクションから安全でした。接続は自動的に処理されます。.Include を使用して、必要に応じて同じ接続内の関連オブジェクトを取得できます。

于 2012-11-12T05:12:24.790 に答える