문제

사전이 다음과 같이 정의 된 코드가 있습니다.

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를 둘러싼 다소 열악한 문서 (Ienumerible 이상의 필요성이 필요함)와 사전의 내용을 구문 분석해야하기 때문에 사전을 통해 DataTable로 고리하기로 결정했습니다. .

다른 팁

@narayanamarthi와 @chrisgessler는 모두 사용과 관련하여 올바른 길을 가고 있습니다. IEnumerable<SqlDataRecord> 데이터 가능 대신 인터페이스. 컬렉션을 데이터 테이블에 복사하는 것은 CPU와 메모리를 낭비하는 것입니다. 그러나 반복자는 컬렉션과 분리 될 수 있습니다.

그래서 일부 참고 사항 :

  • 사용하지 마십시오 AddWithValue 생성 할 때 SqlParameter에스. 그것은 단지 나쁜 pratice입니다
  • TVP에서 원하는 3 가지 뚜렷한 값을 문자열 또는 StringBuilder로 연결하지 마십시오. TVP의 전체 목적은 강력하게 유형 된 레코드로 전달하여 필드를 CSV 목록으로 직렬화하는 것이 목적을 물리칩니다. 대신 Chris가 권장하는 송장 클래스를 사용하십시오.
  • 당신을 교체하십시오 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 한 번의 작업으로 보내지 만 사전 대신 DataTable 또는 IDATAREADER를 전달해야합니다.u003Cint, StringBuilder> 또한 저장된 절차를 사용하는 대신 테이블을 직접 가리켜 야합니다.

컬렉션을 보유한 클래스에서 ienumerable 인터페이스를 구현 한 다음 ienemerable을위한 getEnumerator 메소드를 명시 적으로 구현하는 것이 목적에 도움이됩니다. 기사가 있습니다 http://lennilobel.wordpress.com/2009/07/29/sql-server-2008-table-valued-parameters-a-custom-eterators-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;
    }
}

그런 다음 사용자 정의 목록을 a로 전달합니다 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