2010-05-08 37 views
7

Tôi đang nhập một tệp hóa đơn phẳng vào cơ sở dữ liệu bằng C#. Tôi đang sử dụng TransactionScope để cuộn lại toàn bộ hoạt động nếu gặp sự cố.Giao dịch trong một Giao dịch trong C#

Đây là tệp đầu vào phức tạp, trong đó một hàng không cần thiết bằng một bản ghi. Nó cũng bao gồm các bản ghi liên kết. Hóa đơn sẽ có dòng tiêu đề, chi tiết đơn hàng và sau đó là một dòng tổng. Một số hóa đơn sẽ cần phải được bỏ qua, nhưng tôi có thể không biết nó cần phải được bỏ qua cho đến khi tôi đạt đến tổng số dòng.

Một chiến lược là lưu trữ tiêu đề, chi tiết đơn hàng và tổng dòng trong bộ nhớ và lưu mọi thứ khi đạt đến tổng số dòng. Tôi đang theo đuổi điều đó ngay bây giờ.

Tuy nhiên, tôi đã tự hỏi nếu nó có thể được thực hiện theo một cách khác. Tạo giao dịch "lồng nhau" xung quanh hóa đơn, chèn hàng tiêu đề và chi tiết đơn hàng, sau đó cập nhật hóa đơn khi đạt đến tổng số dòng. Giao dịch "lồng nhau" này sẽ quay trở lại nếu xác định hóa đơn cần được bỏ qua, nhưng giao dịch tổng thể sẽ tiếp tục.

Điều này có thể, thiết thực và bạn sẽ thiết lập điều này như thế nào?

Trả lời

3

Điều này được thực hiện với transaction savepoint. Nó thường trông giống như sau:

BEGIN TRANSACTION 
for each invoice 
    SAVE TRANSACTION InvoiceStarted 
    BEGIN TRY 
    Save header 
    Save line 1 
    Save line 2 
    Save Total 
    END TRY 
    BEGIN CATCH 
    ROLLBACK TO Invoicestarted 
    Log Failed Invoice 
    END CATCH 
end for 
COMMIT 

Tôi đã sử dụng mã giả dựa trên Transact-SQL và đây không phải là tai nạn. Savepoints là một khái niệm cơ sở dữ liệu và các giao dịch Net không hỗ trợ chúng. Bạn có thể sử dụng SqlTransaction trực tiếp và tận dụng SqlTransaction.Save hoặc bạn có thể sử dụng các thủ tục lưu sẵn T-SQL được mô hình hóa sau exception safe template. Tôi khuyên bạn nên tránh các giao dịch .Net (ví dụ: TransactionScope) trong trường hợp này.

+0

+1 - điểm lưu trữ là lựa chọn hoàn hảo cho việc này –

2

Thay vì sử dụng giao dịch lồng nhau, bạn có thể tạo giao dịch trên mỗi hóa đơn. Bằng cách này, chỉ những cập nhật thành công cho toàn bộ hóa đơn mới xuất hiện.

Nếu bạn lồng ghép các giao dịch theo cách bạn mô tả, bạn có nguy cơ có toàn bộ tập dữ liệu được khôi phục, đó không phải là những gì bạn muốn.

+0

Tôi KHÔNG muốn mọi thứ được khôi phục nếu có sự cố. – Rosco

2

Cá nhân, trước tiên tôi sẽ xem hóa đơn có cần phải được thêm vào hay không - nếu có, sau đó thực hiện chèn (trong giao dịch). Nếu không, chỉ cần chuyển sang hóa đơn tiếp theo.

Tôi không nghĩ rằng nó tuyệt vời để chèn và sau đó thực hiện khôi phục theo cách bạn mô tả.

+0

+1 cho giải pháp KISS. Tôi muốn xử lý toàn bộ tập tin đầu tiên vào một khu vực dàn dựng (hoặc trong bộ nhớ, hoặc nếu nó là một bảng dàn dựng lớn trong cơ sở dữ liệu), sau đó lưu dữ liệu hợp lệ vào bảng chính. – Joe

+0

Cho đến nay giữ hóa đơn hiện tại và chi tiết đơn hàng được liên kết trong bộ nhớ đang hoạt động. Tôi đang sử dụng Linq2SQL để lưu trữ chúng vì vậy tôi không phải viết nhiều SQL để xử lý đồ thị đối tượng. Sau khi xác định hóa đơn sẽ được lưu, nó sẽ gửi và phân phối bối cảnh dữ liệu. – Rosco

0

Giao dịch nội bộ không thành công sẽ quay trở lại giao dịch bên ngoài, vì vậy bạn không thể đi tuyến đường đó.

Bạn có thể giả mạo nó bằng cách sử dụng bảng tạm thời (hoặc tải). Chèn từng hóa đơn giao dịch vào bảng tải, và sau đó di chuyển từ bảng tải lên bảng vĩnh viễn một cách nguyên tử.

29

Cả TransactionScope cũng như SQL Server đều không hỗ trợ các giao dịch lồng nhau.

Bạn có thể lồng TransactionScope trường hợp, nhưng chỉ có giao diện bên ngoài của giao dịch lồng nhau. Trong thực tế, có một cái gì đó được gọi là một giao dịch "môi trường xung quanh", và có thể chỉ có một tại một thời điểm. Giao dịch nào là giao dịch môi trường phụ thuộc vào những gì bạn sử dụng cho TransactionScopeOption khi bạn tạo phạm vi.

Để giải thích một cách chi tiết hơn, hãy xem xét những điều sau đây:

using (var outer = new TransactionScope()) 
{ 
    DoOuterWork(); 

    using (var inner1 = new TransactionScope(TransactionScopeOption.Suppress)) 
    { 
     DoWork1(); 
     inner1.Complete(); 
    } 

    using (var inner2 = new TransactionScope(TransactionScopeOption.RequiresNew)) 
    { 
     DoWork2(); 
     inner2.Complete(); 
    } 

    using (var inner3 = new TransactionScope(TransactionScopeOption.Required)) 
    { 
     DoWork3(); 
     inner3.Complete(); 
    } 

    outer.Complete(); 
} 

Đây là những gì xảy ra cho mỗi phạm vi bên trong:

  • inner1 được thực hiện trong một giao dịch tiềm ẩn, độc lập với outer . Không có gì xảy ra trong DoWork1 được đảm bảo là nguyên tử. Nếu điều này không thành công, bạn sẽ có dữ liệu không nhất quán. Bất kỳ công việc nào xảy ra ở đây luôn được cam kết, bất kể điều gì xảy ra với outer.

  • inner2 được thực hiện trong giao dịch mới, độc lập với outer. Đây là giao dịch khác nhau từ outer nhưng đó là không phải là lồng nhau. Nếu nó không thành công, công việc đã xảy ra trong outer (DoOuterWork()) và bất kỳ phạm vi nào khác vẫn có thể được cam kết, nhưng đây là chà: Nếu hoàn thành, sau đó quay trở lại toàn bộ giao dịch outer sẽ không hoàn thành công việc bên trong inner2. Đây là lý do tại sao nó không thực sự lồng nhau. Ngoài ra, inner2 sẽ không có quyền truy cập vào bất kỳ hàng nào bị khóa bởi outer, vì vậy bạn có thể kết thúc với deadlocks tại đây nếu bạn không cẩn thận.

  • inner3 được thực hiện trong cùng một giao dịchouter. Đây là hành vi mặc định. Nếu DoWork3() không thành công và inner3 không bao giờ hoàn tất, thì toàn bộ giao dịch outer sẽ được khôi phục. Tương tự, nếu inner3 hoàn tất thành công nhưng outer được khôi phục, thì mọi công việc được thực hiện trong DoWork3() cũng sẽ được khôi phục.

Vì vậy, bạn có thể hy vọng rằng không có tùy chọn nào trong số này được lồng trong thực tế và sẽ không cung cấp cho bạn những gì bạn muốn. Tùy chọn Required xấp xỉ một giao dịch lồng nhau, nhưng không cung cấp cho bạn khả năng độc lập cam kết hoặc cuộn lại các đơn vị công việc cụ thể bên trong giao dịch.

Điều gần nhất bạn có thể nhận được với các giao dịch lồng nhau thực sự trong SQL Server là câu lệnh SAVE TRAN kết hợp với một số khối TRY/CATCH. Nếu bạn có thể đặt logic của bạn bên trong một hoặc nhiều thủ tục lưu trữ, đây sẽ là một lựa chọn tốt.

Nếu không, bạn sẽ cần phải sử dụng các giao dịch riêng biệt cho mỗi hóa đơn theo đề xuất của Oded.

+0

Cảm ơn các chi tiết. Điều này không rõ ràng trong tài liệu MSDN. – Rosco

+0

+1 cho lời giải thích rất tuyệt vời và rất kỹ lưỡng. Làm tốt lắm, thưa ngài. –

+0

+1 Giống như @Remi - lời giải thích rất hay, và nó đã giúp tôi tiết kiệm tối nay. Chúc tôi có thể + Thêm nữa! Cảm ơn! – Catchops