質問

辞書が次のように定義されているコードがあります

Dictionary<int, StringBuilder> invoiceDict = new Dictionary<int, StringBuilder>();

辞書の各KeyValuePairの各値は、実際には次のように現在作成されている3つの個別の値です。

invoiceDict.Add(pdfCount+i, new StringBuilder(invoiceID.Groups[1].ToString() + "|" + extractFileName + "|" + pdfPair.Key));

ご覧のとおり、3つの値は「|」で区切られています。 「行」の数辞書内の範囲は600〜1200です。テーブル値パラメーターを使用して、1回の操作でSQL Server 2008 DBのすべての値を取得します。

テーブル値パラメーターは次のように定義されます。

CREATE TYPE dbo.BatchSplitterInvoices AS TABLE
(
    InvoiceID varchar(24),
    NotePath varchar(512),
    BatchID varchar(50)
)

CREATE PROCEDURE dbo.cms_createBatchSplitterInvoices (
  @Invoices dbo.BatchSplitterInvoices READONLY,
  @StaffID int
)

辞書からテーブル値パラメーターに渡すことができるものに到達するための最良の方法は何ですか?辞書以外のものを使用する必要がありますか?または、辞書を別のより良いデータ構造に解析する必要がありますか?

SqlParameter invoicesParam = cmd.Parameters.AddWithValue("@Invoices", invoiceDict.Values);
invoicesParam.SqlDbType = SqlDbType.Structured;

ありがとう。

役に立ちましたか?

解決

TVP(IEnumerableだけでなく、それ以上のものが必要です)を取り巻くやや劣ったドキュメントを発行し、とにかく辞書の内容を解析する必要があるため、辞書をループしてDataTableにすることを決定しました。 TVPをフィードします。

他のヒント

DataTableの代わりに IEnumerable&lt; SqlDataRecord&gt; インターフェースを使用することに関して、@ narayanamarthiと@ChrisGesslerの両方が正しい軌道に乗っています。コレクションをDataTableにコピーすると、CPUとメモリが無駄に消費されるだけです。ただし、イテレータはコレクションから分離できます。

注意事項:

  • SqlParameter の作成時に AddWithValue を使用しないでください。それはただの悪い習慣です
  • TVPで必要な3つの異なる値をStringまたはStringBuilderに連結しないでください。 TVPの全体的な目的は、厳密に型指定されたレコードを渡すことであるため、フィールドをCSVリストにシリアル化すると目的が無効になります。代わりに、Chrisが推奨するInvoiceクラスを使用してください。
  • invoiceDict.Add(...) List&lt; Invoice&gt; .Add(new Invoice(...));
  • に置き換えます
  • コレクションを反復処理するメソッドを作成します。

    private static IEnumerable<SqlDataRecord> SendRows(List<Invoice> Invoices)
    {
      SqlMetaData[] _TvpSchema = new SqlMetaData[] {
        new SqlMetaData("InvoiceID", SqlDbType.Int),
        new SqlMetaData("NotePath", SqlDbType.VarChar, 512),
        new SqlMetaData("BatchID", SqlDbType.VarChar, 50)
      };
      SqlDataRecord _DataRecord = new SqlDataRecord(_TvpSchema);
    
      foreach(Invoice _Invoice in Invoices)
      {
        _DataRecord.SetInt32(0, _Invoice.Id);
        _DataRecord.SetString(1, _Invoice.NotePath);
        _DataRecord.SetString(2, _Invoice.BatchId);
        yield return _DataRecord;
      }
    }
    
  • 次のようにパラメーターを宣言します。

    SqlParameter _InvoiceParam = cmd.Parameters.Add("@Invoices", SqlDbType.Structured);
    _InvoiceParam.Value = SendRows(Invoices);
    

TVP全般に関して、次の2つの回答に追加のメモとリンクがあります。

SqlBulkCopy が送信します1つの操作ですが、Dictionary&lt; int、StringBuilder&gt;の代わりにDataTableまたはIDataReaderを渡す必要があります。また、ストアドプロシージャを使用する代わりに、テーブルを直接指す必要があります。

コレクションを保持するクラスにIEnumerableインターフェイスを実装し、IEnemerableのGetEnumeratorメソッドを明示的に実装することで目的を果たします。 http://lennilobel.wordpress.com/2009/07/29/sql-server-2008-table-valued-parameters-and-c-custom-iterators- a-match-made-in-heaven / 。これを実行してください

カスタムコレクションオブジェクトに IEnumerable&lt; SqlDataRecord&gt; を実装する必要があります。あなたの場合、 Dictionary&lt; int、StringBuilder&gt; List&lt; Invoice&gt; または List&lt; Tuple&lt; int、string、int&gt;&gt; <に変更する必要があります

例:

class Invoice
{
  public int Id { get; set; }
  public string NotePath { get; set; }
  public int BatchId { get; set; }
}

class InvoiceCollection : List<Invoice>, IEnumerable<SqlDataRecord>
{
  IEnumerator<SqlDataRecord> IEnumerable<SqlDataRecord>.GetEnumerator()
  {
    SqlDataRecord r - new SqlDataRecord(
      new SqlMetaData("InvoiceId", SqlDataType.Int), 
      new SqlMetaData("NotePath", SqlDataType.VarChar), 
      new SqlMetaData("BatchId", SqlDataType.Int)
    );

    foreach(var item in this)
    {
      r.SetInt32(0, item.Id);
      r.SetString(1, item.NotePath);
      r.SetInt32(2, item.BatchId);
      yield return r;
    }
}

次に、カスタムリストを SqlParameter に渡すだけです:

SqlCommand cmd = new SqlCommand("dbo.InvoiceInsUpd", connection);
cmd.CommandType = CommandType.StoredProcedure;

SqlParameter sqlParam = cmd.Parameters.AddWithValue("@Invoices", invoices);
sqlParam.SqlDbType = SqlDbType.Structured;
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top