2

最小限のボイラープレートコードを使用してデータベースクエリを実行するための最良の方法を見つけようとしています。SqlCommandドキュメントで提案されている方法:

private static void ReadOrderData(string connectionString)
{
    string queryString = "SELECT OrderID, CustomerID FROM dbo.Orders;";
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        SqlCommand command = new SqlCommand(queryString, connection);
        connection.Open();
        SqlDataReader reader = command.ExecuteReader();
        try
        {
            while (reader.Read())
            {
                Console.WriteLine(String.Format("{0}, {1}",     reader[0], reader[1]));
            }
        }
        finally
        {
            reader.Close();
        }
    }
}

ほとんどの場合、データベースと対話するすべてのメソッドで繰り返す必要のあるコードで構成されています。

私はすでに接続の確立を除外する習慣があります。これにより、次のようなコードが生成されます。(例を少し簡単にするために、データを返すように変更しています。)

private SQLConnection CreateConnection()
{
    var connection = new SqlConnection(_connectionString);
    connection.Open();
    return connection;
}

private List<int> ReadOrderData()
{
    using(var connection = CreateConnection())
    using(var command = connection.CreateCommand())
    {
        command.CommandText = "SELECT OrderID FROM dbo.Orders;";

        using(var reader = command.ExecuteReader())
        {
            var results = new List<int>();
            while(reader.Read()) results.Add(reader.GetInt32(0));
            return results;
        }
    }
}

それは改善ですが、それでも私を悩ませるのに十分な定型文があります。これをさらに減らすことはできますか?特に、手順の最初の2行について何かしたいと思います。メソッドがの作成を担当する必要があるとは思わないSqlCommand。例のように小さな繰り返しですが、トランザクションが手動で管理されているか、タイムアウトが変更されているか、またはそのようなものである場合、それは大きくなるようです。

編集:少なくとも仮定的には、返されるさまざまなタイプのデータの束が必要になると想定します。その結果、ソリューションは1つのサイズですべてに対応する方法ではなく、少なくとも、、、、、または他のいずれかが呼び出されているかどうかExecuteNonQueryに応じてExecuteScalar、いくつかの異なる方法が必要になります。それらの間の繰り返しを減らしたいと思います。ExecuteReaderExecuteReaderAsync

4

5 に答える 5

5

Dapperを試しましたか?

確かに、これではDataReaderは取得できませんが、一度試してみると、この方法を好むかもしれません。

それは、ORMと呼ばれている間にORMが可能な最も軽量なものです。DataReaderとストロングタイプの間でマッピングするメソッドはもうありません。

ここですべてのStackExchangeサイトで使用されます。

using (var conn = new SqlConnection(cs))
{
    var dogs = connection.Query("select name, age from dogs");

    foreach (dynamic dog in dogs)
    {
        Console.WriteLine("{0} age {1}", dog.name, dog.age);
    }
}

また

using (var conn = new SqlConnection(cs))
{
    var dogs = connection.Query<Dog>("select Name, Age from dogs");

    foreach (Dog dog in dogs)
    {
        Console.WriteLine("{0} age {1}", dog.Name, dog.Age);
    }
}

class Dog
{
    public string Name { get; set; }
    public int Age { get; set; }
}
于 2012-09-24T21:47:42.553 に答える
2

自分でデータアクセスをロールしたい場合は、このヘルプメソッドのパターンが重複を取り除く1つの方法になる可能性があります。

private List<int> ReadOrderData()
{
    return ExecuteList<int>("SELECT OrderID FROM dbo.Orders;", 
        x => x.GetInt32("orderId")).ToList();
}

private IEnumerable<T> ExecuteList(string query, 
    Func<IDataRecord, T> entityCreator)
{
    using(var connection = CreateConnection())
    using(var command = connection.CreateCommand())
    {
        command.CommandText = query;
        connection.Open();
        using(var reader = command.ExecuteReader())
        {
            while(reader.Read()) 
               yield return entityCreator(reader);
        }
    }
}

パラメータのサポートを追加する必要があり、これはコンパイルされない可能性がありますが、パターンは私が説明しようとしているものです。

于 2012-09-24T21:33:26.687 に答える
1

私が通常行うことは、SQL文字列を受け入れる、しばらく前に作成したカスタムクラスと、オプションでパラメーターのリストを使用することです。これにより、DataTableが返されます。

呼び出し間で変化するのは、通常、最適なIMHOであるSQLだけです。

本当にDataReaderを使用する必要がある場合は、次のようにすることができます。

public void ExecuteWithDataReader(string sql, Action<DataReader> stuffToDo) {
    using (SqlConnection connection = new SqlConnection(connectionString)) {
        using (SqlCommand command = new SqlCommand(sql, connection)) {
            connection.Open();

            using (SqlDataReader reader = command.ExecuteReader()) {
                try {
                    while (reader.Read()) {
                        stuffToDo(reader);
                    }
                }
                finally {
                    reader.Close();
                }
            }
        }
    }
}


private static void ReadOrderData(string connectionString) {
    string sql = "SELECT OrderID, CustomerID FROM dbo.Orders;";

    ExecuteWithDataReader(sql, r => Console.WriteLine(String.Format("{0}, {1}", r[0], r[1])));
}
于 2012-09-24T21:25:06.210 に答える
0

最初の2行は、必要な最も重要なものです...

しかし、それでもやりたい場合は、データベースハンドラークラスに変えることができます。そうです、それはより多くのコードになりますが、リファクタリングの概念では、すべてが関連トピックに移動します...

コマンドを受け取り、アクションを実行するシングルトンクラスを作成してみてください。そのため、タイプSqlDataReaderリーダーの結果を返します。

于 2012-09-24T21:35:41.493 に答える
0

コメントでこれを行うのは多すぎました。

ボイラープレートコードをお勧めします

    using(conn = new sqlconnection)
    using(cmd = new sqlcommand) {
          // blah blah blah
    }

軽く削除するものではなく、代わりに、現在の場所に正確に保持することをお勧めします。リソース、特に管理されていないリソースは、可能な限り実行に最も近いポイントで開いて解放する必要があります。

他の開発者が適切なクリーンアップ規則に従わないことが容易であるため、少なからずあります。

あなたが次のようなことをするなら

private SQLConnection CreateConnection()   
{   
    var connection = new SqlConnection(_connectionString);   
    connection.Open();   
    return connection;   
}   

次に、別のプログラマーを招待してこのメ​​ソッドを呼び出し、クエリが実行されるとすぐにリソースの解放に完全に失敗します。作成しているアプリの種類はわかりませんが、Webアプリでは、これまでに経験したことがない限り、デバッグが困難なタイプのメモリ/接続/リソースエラーが発生します。

代わりに、Dapper.netなどの軽量ORMを調べて、それらがどのようにアプローチしたかを確認することをお勧めします。私はdapperを使用していませんが、かなり良いと聞きました。私がそれを使用しない理由は、データベースに対してインラインSQLを実行することを許可しないためです(ただし、これは非常に異なる会話です)。


これが私たちの標準です:

public static DataTable StatisticsGet( Guid tenantId ) {
    DataTable result = new DataTable();
    result.Locale = CultureInfo.CurrentCulture;

    Database db = DatabaseFactory.CreateDatabase(DatabaseType.Clients.ToString());

    using (DbCommand dbCommand = db.GetStoredProcCommand("reg.StatsGet")) {
        db.AddInParameter(dbCommand, "TenantId", DbType.Guid, tenantId);

        result.Load(db.ExecuteReader(dbCommand));
    } // using dbCommand

    return result;
} // method::StatisticsGet

エンタープライズライブラリを多用しています。それは短く、シンプルで、要点があり、非常によくテストされています。このメソッドはデータテーブルを返すだけですが、オブジェクトコレクションを簡単に返すことができます。

于 2012-09-24T22:11:27.787 に答える