2

次のように定義された Dictionary を持つコードがあります。

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 の範囲です。テーブル値パラメーターを使用して、SQL Server 2008 DB のすべてを 1 回の操作で取得したいと考えています。

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

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;

ありがとう。

4

4 に答える 4

1

TVP に関するドキュメントがやや貧弱で (IEnumerable だけでは不十分)、とにかく辞書の内容を解析する必要があるため、Dictionary を DataTable にループすることにしました。 .

于 2009-09-21T18:45:15.903 に答える
1

IEnumerable<SqlDataRecord>DataTable の代わりにインターフェイスを使用することに関して、@narayanamarthi と @ChrisGessler の両方が正しい方向に進んでいます。コレクションを DataTable にコピーすることは、CPU とメモリを無駄に浪費するだけです。ただし、反復子はコレクションから分離できます。

したがって、いくつかのメモ:

  • AddWithValueの作成時には使用しないでくださいSqlParameter。それはただの悪い習慣です
  • TVP に必要な 3 つの異なる値を String または StringBuilder に連結しないでください。TVP の全体的な目的は、厳密に型指定されたレコードを渡すことであるため、フィールドを CSV リストにシリアル化すると目的が無効になります。代わりに、Chris が推奨する Invoice クラスを使用してください。
  • invoiceDict.Add(...)に置き換えますList<Invoice>.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つの回答に追加のメモとリンクがあります。

于 2015-02-24T17:32:35.333 に答える
0

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

例えば:

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;
于 2013-02-26T13:16:33.327 に答える
0

コレクションを保持するクラスに 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に記事があります/ . これを通過してください

于 2009-10-26T04:53:26.383 に答える