사전 에서 테이블 값 sqlparameter로 이동합니다. 어떻게?
-
06-07-2019 - |
문제
사전이 다음과 같이 정의 된 코드가 있습니다.
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;