0

このコードの一部を単純なDALに抽象化するためのより良い方法は何でしょうか。現時点では、コードにパッチを適用しているだけで、EF、Linq2Sql、またはORMを使用する時間や必要はありません。

    public string GetMySpecId(string dataId)
    {
        using (SqlConnection conn = new SqlConnection(this.MyConnectionString))
        {

            conn.Open();

            // Declare the parameter in the query string
            using (SqlCommand command = new SqlCommand(@"select ""specId"" from ""MyTable"" where ""dataId"" = :dataId", conn))
            {
                // Now add the parameter to the parameter collection of the command specifying its type.
                command.Parameters.Add(new SqlParameter("dataId", SqlDbType.Text));

                command.Prepare();

                // Now, add a value to it and later execute the command as usual.
                command.Parameters[0].Value = dataId;


                using (SqlDataReader dr = command.ExecuteReader())
                {
                    while (dr.Read())
                    {
                        specId = dr[0].ToString();
                    }
                }
            }
        }

        return specId;
    }

GetMySpecId()から接続やコマンドなどを引き出すためのクリーンな方法は何ですか。これらの関数がたくさんあり、using....を何度も書きたくないからです。

4

5 に答える 5

2

そうですね、すべてのものをカプセル化してDataTableを返す独自のカスタムデータアクセスヘルパーを作成できます。

public string GetMySpecId(string dataId)
{
    DataTable result = _dbHelper.ExecuteQuery(
        @"select ""specId"" from ""MyTable"" where ""dataId"" = :dataId",
        new SqlParameter("dataId", dataId);
    return result.Rows[0][0].ToString();
}

または、DataReaderを使用するという考えに固執している場合は、デリゲートをヘルパーに渡すことができます。ヘルパーは、usingステートメント内で呼び出されます。

public string GetMySpecId(string dataId)
{
    return _dbHelper.ExecuteQuery(
        dr => 
           {
               if(dr.Read())
               {
                   return dr[0].ToString();
               }
               // do whatever makes sense here.
           },
        @"select ""specId"" from ""MyTable"" where ""dataId"" = :dataId",
        new SqlParameter("dataId", dataId));
}

Dapperのような軽量ツールを使用して、構文の一部を単純化し、データ型へのマッピングを処理することもできます。(接続を開くなどの処理を行う必要があります。)

アップデート

2番目の例で使用されるExecuteQueryメソッドを作成する方法の例を次に示します。

public T ExecuteQuery<T>(
    Func<IDataReader, T> getResult,
    string query,
    params IDataParameter[] parameters)
{
    using (SqlConnection conn = new SqlConnection(this.MyConnectionString))
    {
        conn.Open();
        // Declare the parameter in the query string
        using (SqlCommand command = new SqlCommand(query, conn))
        {
            foreach(var parameter in parameters)
            {
                command.Parameters.Add(parameter);
            }
            command.Prepare();
            using (SqlDataReader dr = command.ExecuteReader())
            {
                return getResult(dr);
            }
        }
    }
}
于 2012-12-04T22:25:18.893 に答える
1

接続、コマンド、およびリーダーオブジェクトをusingステートメント内に保持するために、yieldreturnステートメントを使用できます。

public class ScalarReader<T>
{
    const string MyConnectionString = "...";

    private string _returnColumn, _table, _whereCond;
    private object[] _condParams;

    public ScalarReader(string returnColumn, string table, string whereCond,
                        params object[] condParams)
    {
        _returnColumn = returnColumn;
        _table = table;
        _whereCond = whereCond;
        _condParams = condParams;
    }

    public IEnumerator<T> GetEnumerator()
    {
        using (SqlConnection conn = new SqlConnection(MyConnectionString)) {
            conn.Open();
            string select = String.Format(@"SELECT ""{0}"" FROM ""{1}"" WHERE {2}",
                                          _returnColumn, _table, _whereCond);
            using (SqlCommand command = new SqlCommand(select, conn)) {
                for (int p = 0; p < _condParams.Length; p++) {
                    command.Parameters.AddWithValue("@" + (p+1), _condParams[p]);
                }
                using (SqlDataReader dr = command.ExecuteReader()) {
                    while (dr.Read()) {
                        if (dr.IsDBNull(0)) {
                            yield return default(T);
                        } else {
                            yield return (T)dr[0];
                        }
                    }
                }
            }
        }
    }
}

あなたはそれをこのように呼ぶでしょう

var reader = new ScalarReader<string>("specId", "MyTable", "dataId=@1", "x");
foreach (string id in reader) {
    Console.WriteLine(id);
}

パラメータ名に規則を使用していることに注意してください。それらはと名付けられて@1, @2, @3 ...います。

var reader =
    new ScalarReader<DateTime>("date", "MyTable", "num=@1 AND name=@2", 77, "joe");
于 2012-12-04T22:57:24.467 に答える
0

usingステートメントの途中からIDataReaderを返す必要があります。これを行うとすぐに、接続とデータが失われます。あなたは本当にあなたが求めていることをすることはできません。

于 2012-12-04T22:19:34.147 に答える
0

実際のコードがないので申し訳ありませんが、このようなことを行うことができますが、それはあなたにアイデアを与えるでしょう。もちろん、object []を何か便利なものに戻すには注意が必要ですが、specId = dr [0] .ToString();を使用してすでにそれを行っているようなものです。

class MyDb
{
    public MyDb()
    {
    }

    public void Initialize()
    {
        // open the connection
    }

    public void Finalize()
    {
        // close the connection
    }

    public List<object[]> Query(string command, List<SqlParameter> params)
    {
        // prepare command
        // execute reader
        // read all values into List of object[], and return it
    }
}
于 2012-12-04T22:29:34.187 に答える
0

次のように、すべての使用法と基本コードを備えた基本関数を持つ基本抽象クラスを作成できます。

public abstract class BaseClass
{
  public abstract void myFunc(SqlConnection conn);

  public void BaseFunc()
  {      
        using (SqlConnection conn = new SqlConnection(this.MyConnectionString))
        {
            conn.Open();
            myFunc(conn);
            ..any other base implementation...
        }
  }
}

各派生クラスはBaseClassを継承し、各派生クラスの特定のクエリとすべての特定のものを使用して抽象MyFuncを実装します。基本抽象クラスのBaseFunc関数の外部から呼び出します。

于 2012-12-04T22:30:28.690 に答える