2011-01-18 20 views
0

私はいくつかのストアドプロシージャを呼び出すためにC#SQL Serverラッパーを書くことを検討しています。私は、単一の関数を書いていた場合、私は(私は/正しい適切だと思います)、次のような何かしたい:.net SQL Serverは、procsラッパークラス構造を格納しました

void RunStoredProc1(object arg1) 
{ 
    using(SqlConnection conn = new SqlConnection(connStr)){ 
    try{ 
     SqlCommand cmd = new SqlCommand("storedProc1", conn); 
     cmd.CommandType = CommandType.StoredProcedure; 
     cmd.Parameters.AddWithValue("@input1", arg1); 

     conn.Open(); 
     cmd.ExecuteNonQuery(); 
    } catch (Exception ex){ 
     //handle the exception appropriately. 
    } 
    } 
} 

私がいる問題は、それが繰り返される多くのコードのように思えるということです。..すべての関数は同じコードを使用する/開こうとする(実行する/実行する)/ catchするコードであり、すべてを1つの場所に収めるのがよいでしょう。これを行うきれいな方法がありますか?データ・リーダーを使用したいと思う質問はどうですか?

答えて

5

何かが何をすべきかなりストレートフォワード機能の混乱を作ることになります

void RunStoredProc(string storedProcName, IDictionary<string, object> args) 
{ 
    using (SqlConnection conn = new SqlConnection(connStr)) 
    using (SqlCommand cmd = conn.CreateCommand()) 
    { 
     cmd.CommandText = storedProcName; 
     cmd.CommandType = CommandType.StoredProcedure; 

     foreach (KeyValuePair<string, object> kvp in args) 
     { 
      cmd.Parameters.AddWithValue(kvp.Key, kvp.Value); 
     } 

     conn.Open(); 
     cmd.ExecuteNonQuery(); 
    } 
} 

接続オブジェクト自体はおそらく、このヘルパーメソッドのパラメータとしての方が良いかもしれないので、staticにすることができます。 SqlConnectionに拡張メソッドとして書くのは興味深いかもしれません。

私はあなたのRunStoredProc1メソッドで例外処理を続けるでしょう。あるいは、RunStoredProc1を呼び出すメソッドで、例外処理が異なる場合があります。

void RunStoredProc1(object input1) 
{ 
    var args = new Dictionary<string, object>() 
       { 
        { "input1", input1 } 
       }; 

    try 
    { 
     RunStoredProc("storedProc1", args); 
    } 
    catch (Exception ex) 
    { 
     // Handle exception properly 
    } 
} 
+0

+1 - このコードを書いただけで、かなり逐語的に投稿されました。 –

+0

これは非常にクリーンなソリューションのようです。ありがとう! 'SqlDataReader'を開くと同様のことができるでしょうか? – chezy525

+0

@ chezy525:私はこれらの方法をいくつか持っていました。基本的には 'ExecuteDataReader'と' ExecuteScalar'です。出力パラメータを使用する場合は、わずかに異なるソリューションも必要になります。上記のエンタープライズライブラリは、多くの柔軟性が必要な場合にはオプションとなります。 – Thorarin

0

は個人的に、私はあなたがあなたのリソースを管理するために必要であろうと最終的には、従来のトライキャッチにわたり

void RunStoredProc1(object arg1) 
{ 
    try 
    { 
     using(SqlConnection conn = new SqlConnection(connStr)) 
     { 
      using SqlCommand cmd = new SqlCommand("storedProc1", conn)) 
      { 
       cmd.CommandType = CommandType.StoredProcedure; 
       cmd.Parameters.AddWithValue("@input1", arg1); 

       conn.Open(); 
       cmd.ExecuteNonQuery(); 
      } 
     } 
    } 
    catch (Exception ex) 
    { 
     //handle the exception appropriately. 
    } 
} 

を好みます。

しかし全体的に、私は別の方法でそれをやりたいので、sprocのキャッチブロックをカスタマイズすることができます。

また、あなたは道複数のパラメータを必要とするかもしれません、あなたはちょうどこのよう

+0

'SqlCommand'の使用ブロックを除いて、元のコードと大きな違いはありません。ベストプラクティスを指摘していますが、実際の質問には対応していませんか? – Thorarin

+0

コードブロックの後のテキストは、OPが尋ねた質問に対する直接的な回答です。 –

+0

さらに、引数のリストをループすることで、引数のサイズを非常に簡単に定義することはできません。 –

1

マイクロソフトエンタープライズライブラリデータアクセスアプリケーションブロックは、あなたのデータ層のための純粋なADO.NETに付着している場合、そのような冗長なコードを減らすのに役立つことができます。 http://msdn.microsoft.com/en-us/library/ff664408(v=PandP.50).aspxを参照してください。オンラインでダウンロードしたコードサンプルも多く、つまりhttp://msdn.microsoft.com/en-us/library/ff664702(v=PandP.50).aspxです。

1

私はコンピューターにロットや繰り返し作業をさせることの大きなファンです。彼らはそれをとてもうまく使っています。私は一度それをするようにそれらを教えなければならない。だから私は、厳密に型付けされたアクセスコードを生成するために参照データベースを使用するコードジェネレータを書いた。この手法の利点は、ストアドプロシージャのシグネチャを変更した場合、データアクセスレイヤの再作成がすべて必要になることです。どんな急な変更もコンパイルエラーの原因になります。

私が書いたコード生成は、目的のストアドプロシージャを識別するXMLファイルを読み込み、指定された参照データベースからメタデータを取得します。

XMLファイルには、各ストアドプロシージャが

  • 複数の結果セット(データセット)を返すかどうかを識別するフラグを含む
  • 単一の行(のDataRow)(データテーブル)
  • 単一の結果セットを単一列の単一行(スカラー値)
  • a DataReader
  • an XmlReader
  • または何もありません

これにより、ストアドプロシージャごとに1つのクラスが生成されます。生成されたコードは、ストアド・プロシージャの戻りコードおよびすべての出力パラメータの戻り値へのアクセスを提供します。

また、ストアドプロシージャのソースコード内のストアドプロシージャの宣言を解析して、省略可能な引数(デフォルト値を持つもの)を識別します。私にとって

public DataTable GetRiskFactorsForPatient(int patientID) 
    { 
     dbo_GetRiskbyPatient sp = new dbo_GetRiskbyPatient(CONNECT_STRING_ID) ; 
     int     rc = sp.Exec(patientID) ; 
     DataTable   dt = sp.ResultSet ; 

     if (dt == null) throw new InvalidOperationException("nothing returned from stored procedure") ; 
     return dt ; 
    } 
2

だけで楽しく運動、必ずしもあなたがそれを実装したいと思います方法:生成されたコードを呼び出す

は次のようになります。私は、SqlCommandsを構築して実行するためのすばやく流暢なインターフェイスを作成しました。

サンプル用法のカップル:

int i = Sql.UsingConnection("sample") 
     .GetTextCommandFor("Select Top 1 ActorID From Actor Where FirstName = @fname") 
     .AddParameters(new {fname = "Bob"}) 
     .OnException(e => Console.WriteLine(e.Message)) 
     .ExecuteScalar<int>(); 

    var q = Sql.UsingConnection("sample") 
     .GetTextCommandFor("Select * From Actor Where [email protected] and ActorID > @id") 
     .AddParameters(new {id = 1000, fname = "Bob"}); 

    using(var reader = q.ExecuteReader()) 
    { 
     while(reader.Read()) 
     { 
      // do something 
     } 
    } 

実際のクラス(複数可)とのインタフェースは以下の通りです:

public class Sql 
{ 
    public static ISqlCommandTypeSelector UsingConnection(string connection) 
    { 
     return new SqlBuilder(connection); 
    } 

    private class SqlBuilder : ISqlCommandTypeSelector, ISqlParameterManager, ISqlExecutor 
    { 
     private string _connection; 
     private string _sqltext; 
     private CommandType _commandtype; 
     private Action<Exception> _exceptionBehavior = DefaultExceptionBehavior; 

     private IList<SqlParameter> _inParams; 


     public SqlBuilder(string connection) 
     { 
      _connection = ConfigurationManager.ConnectionStrings[connection].ConnectionString; 
      _inParams = new List<SqlParameter>(); 
     } 

     public ISqlParameterManager GetTextCommandFor(string text) 
     { 
      _sqltext = text; 
      _commandtype = CommandType.Text; 
      return this; 
     } 

     public ISqlParameterManager GetProcCommandFor(string proc) 
     { 
      _sqltext = proc; 
      _commandtype = CommandType.StoredProcedure; 
      return this; 
     } 

     public ISqlExecutor OnException(Action<Exception> action) 
     { 
      _exceptionBehavior = action; 
      return this; 
     } 

     public void ExecuteNonQuery() 
     { 
      try 
      { 
       using (var connection = new SqlConnection(_connection)) 
       using (var cmd = connection.CreateCommand()) 
       { 
        ConfigureCommand(cmd); 
        PopulateParameters(cmd); 
        connection.Open(); 
        cmd.ExecuteNonQuery(); 
       } 
      } 
      catch(Exception ex) 
      { 
       _exceptionBehavior(ex); 
      } 
     } 

     public T ExecuteScalar<T>() 
     { 
      T result = default(T); 
      try 
      { 
       using (var connection = new SqlConnection(_connection)) 
       using (var cmd = connection.CreateCommand()) 
       { 
        ConfigureCommand(cmd); 
        PopulateParameters(cmd); 
        connection.Open(); 
        result = (T) cmd.ExecuteScalar(); 
        return result; 
       } 
      } 
      catch(InvalidCastException ex) 
      { 
       // rethrow? 
      } 
      catch(Exception ex) 
      { 
       _exceptionBehavior(ex); 
      } 
      return result; 
     } 

     public IDataReader ExecuteReader() 
     { 
      try 
      { 
       var connection = new SqlConnection(_connection); 
       var cmd = connection.CreateCommand(); 
       ConfigureCommand(cmd); 
       PopulateParameters(cmd); 
       connection.Open(); 
       var reader = cmd.ExecuteReader(CommandBehavior.CloseConnection); 
       return reader; 
      } 
      catch(Exception ex) 
      { 
       _exceptionBehavior(ex); 
      } 
      return null; 
     } 

     public ISqlExecutor AddParameters(object @params) 
     { 
      var type = @params.GetType(); 
      var props = type.GetProperties(); 
      foreach (var propertyInfo in props) 
      { 
       var param = new SqlParameter("@" + propertyInfo.Name, propertyInfo.GetValue(@params, null)); 
       param.Direction = ParameterDirection.Input; 
       _inParams.Add(param); 
      } 
      return this; 
     } 

     public ISqlExecutor WithoutParams() 
     { 
      return this; 
     } 

     private void ConfigureCommand(SqlCommand cmd) 
     { 
      cmd.CommandText = _sqltext; 
      cmd.CommandType = _commandtype; 
     } 

     private void PopulateParameters(SqlCommand cmd) 
     { 
      cmd.Parameters.AddRange(_inParams.ToArray()); 
     } 

     private static void DefaultExceptionBehavior(Exception e) 
     { 
      // do something 
     } 
    } 


} 

public interface ISqlCommandTypeSelector 
{ 
    ISqlParameterManager GetTextCommandFor(string text); 
    ISqlParameterManager GetProcCommandFor(string proc); 
} 

public interface ISqlExecutor 
{ 
    ISqlExecutor OnException(Action<Exception> action); 

    void ExecuteNonQuery(); 
    T ExecuteScalar<T>(); 
    IDataReader ExecuteReader(); 
} 

public interface ISqlParameterManager 
{ 
    ISqlExecutor AddParameters(object @params); 
    ISqlExecutor WithoutParams(); 
} 

本当にあれば、おそらくいくつかのより多くをリファクタリングすることができ、いくつかの繰り返しのコードがあります繰り返しコードが嫌い。これはちょうど楽しい演習ですが、おそらくデータアクセスをどのようにしたいのでしょうか。これは、書き込まれたときのパラメータもサポートしていません。

+0

いいね。愛らしい流暢なインターフェイス – Andrei

関連する問題