سؤال

لدي رمز يحتوي على قاموس محدد على النحو التالي:

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

كل قيمة في كل keyvaluepair يكون القاموس في الواقع ثلاث قيم منفصلة تم إنشاؤها حاليًا على النحو التالي:

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

كما ترون ، يتم فصل القيم الثلاث بواسطة "|". يمكن أن يتراوح عدد "الصفوف" في القاموس بين 600-1200. أرغب في استخدام معلمة ذات قيمة جدول للحصول على كل ذلك في 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;

شكرًا.

هل كانت مفيدة؟

المحلول

بسبب إصداره مع وثائق سيئة إلى حد ما المحيطة بـ TVPs (يحتاج إلى أكثر من مجرد غير قابلة للتطبيق) ، والاضطرار إلى تحليل محتوى القواميس على أي حال ، قررت أن أحلق من خلال القاموس إلى dataTable ، والذي يبدو أنه الكائن المفضل لإطعام TVP .

نصائح أخرى

كلا narayanamarthi و chrisgessler على المسار الصحيح فيما يتعلق باستخدام IEnumerable<SqlDataRecord> واجهة بدلا من dataTable. نسخ المجموعة إلى datatable هو مجرد إهدار وحدة المعالجة المركزية والذاكرة دون زيادة. ولكن يمكن أن يكون التكرار منفصلاً عن المجموعة.

لذلك بعض الملاحظات:

  • لا تستخدم AddWithValue عند إنشاء SqlParameterس. إنه مجرد pratice سيء
  • لا تسلس القيم الثلاثة المميزة التي تريدها في TVP في سلسلة أو StringBuilder. الغرض الكامل من TVP هو تمرير سجل قوي من النوع ، لذا فإن تسلسل الحقول في قائمة CSV يهزم الغرض. بدلاً من ذلك ، استخدم فئة الفاتورة على النحو الموصى به من قبل كريس.
  • استبدال الخاص بك 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);
    

لدي ملاحظات وروابط إضافية في الإجابات التالية فيما يتعلق بـ TVPs بشكل عام:

sqlbulkcopy سوف ترسلها في عملية واحدة ، ولكن ستحتاج إلى تمريرها قابلاً للاختراق أو idatareader بدلاً من القاموسu003Cint, StringBuilder> ستحتاج أيضًا إلى الإشارة مباشرة إلى الجدول ، بدلاً من استخدام إجراء مخزن.

إن تنفيذ واجهة iEnumerable على الفصل الذي يحمل مجموعتك ثم تنفيذ طريقة getEnumerator بشكل صريح سيخدم الغرض. يوجد مقال في http://lennilobel.wordpress.com/2009/07/29/sql-server-2008-table-valued-parameters-and-c-custom-iterators-a-match-made-in-heaven/. من فضلك اذهب من خلال هذا

تحتاج إلى التنفيذ 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;
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top