Frage

Ich habe Code, der ein Wörterbuch hat wie folgt definiert:

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

Jeder Wert in jedem KeyValuePair Wörterbuch ist eigentlich drei getrennte Werte erstellt zur Zeit wie folgt:

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

Wie Sie sehen können, werden die drei Werte durch eine getrennt. Die Anzahl der „Zeilen“ im Wörterbuch kann zwischen 600-1200 reicht. Ich möchte einen Tabellenwertparameter verwenden, um alle, dass in meinem SQL Server 2008 DB in einem Vorgang zu erhalten.

Die Tabellenwertparameter wie folgt definiert ist:

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
)

Was ist der beste Weg, aus dem Wörterbuch, um etwas zu bekommen, die in den Tabellenwert param geben werden können? Soll ich etwas anderes als das Wörterbuch benutzen? Oder muss ich das Wörterbuch in eine andere, bessere Datenstruktur analysieren?

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

Danke.

War es hilfreich?

Lösung

Durch Ausgabe mit etwas schlechten Dokumentation rund um das TVPs (braucht mehr als nur IEnumerable), und mit dem Inhalt der Wörterbücher analysieren sowieso, habe ich beschlossen, eine Schleife durch das Wörterbuch in eine Datentabelle, die das bevorzugte Objekt zu sein scheint speist einen TVP.

Andere Tipps

Sowohl @narayanamarthi und @ChrisGessler ist auf dem richtigen Weg in Bezug der IEnumerable<SqlDataRecord> Schnittstelle statt einem Datatable zu verwenden. Kopieren die Sammlung zu einer Datatable vergeudet nur CPU und Speicher für keinen Gewinn. Aber der Iterator kann aus der Sammlung getrennt sein.

So einige Hinweise:

  • Sie AddWithValue nicht verwenden, wenn SqlParameters zu schaffen. Es ist nur eine schlechte pratice
  • Sie die 3 verschiedene Werte nicht verketten, die Sie in der TVP in einen String oder String wollen. Der gesamte Zweck eines TVP ist in einem stark typisierte Datensatz so die Felder in eine CSV-Liste Serialisierung passieren Niederlagen der Zweck. Statt die Rechnungsklasse verwenden, wie von Chris empfohlen.
  • Ersetzen Sie Ihre invoiceDict.Add(...) mit List<Invoice>.Add(new Invoice(...));
  • Erstellen Sie eine Methode über die Sammlung iterieren:

    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;
      }
    }
    
  • Deklarieren Sie die Parameter wie folgt:

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

Ich habe zusätzliche Hinweise und Links in den folgenden zwei Antworten in Bezug auf TVPs allgemein:

SqlBulkCopy wird es senden in eine Operation, aber Sie müssen es passieren eine Datatable oder eine IDataReader anstelle eines Dictionary und Sie müssen auch direkt auf den Tisch zeigen, anstatt eine gespeicherte Prozedur zu verwenden.

Die Umsetzung die IEnumerable-Schnittstelle auf der Klasse Ihre Sammlung hält und dann explizit die GetEnumerator-Methode für die IEnemerable Umsetzung wird den Zweck dienen. Es gibt einen Artikel unter http://lennilobel.wordpress.com/2009/07/29/sql-server-2008-table-valued-parameters-and-c-custom-iterators- a-match-made-in-Himmel / . Bitte gehen Sie durch diese

Sie müssen IEnumerable<SqlDataRecord> auf einem benutzerdefinierten Sammlungsobjekt implementieren. In Ihrem Fall sollten Sie Ihre Dictionary<int, StringBuilder> ändern List<Invoice> oder List<Tuple<int, string, int>>

Zum Beispiel:

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;
    }
}

Dann passieren einfach die benutzerdefinierte Liste zu einem SqlParameter:

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

SqlParameter sqlParam = cmd.Parameters.AddWithValue("@Invoices", invoices);
sqlParam.SqlDbType = SqlDbType.Structured;
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top