2011-10-07 23 views
5

Tôi đang xây dựng một hệ thống xử lý hàng loạt. Các lô Units có số lượng từ 20-1000. Mỗi Unit về bản chất là một hệ thống phân cấp của các mô hình (một mô hình chính và nhiều mô hình con). Nhiệm vụ của tôi liên quan đến việc lưu từng hệ thống phân cấp mô hình vào cơ sở dữ liệu dưới dạng một giao dịch đơn lẻ (hoặc mỗi hệ thống phân cấp cam kết hoặc phân cấp lại). Thật không may EF không thể xử lý hai phần của hệ thống phân cấp mô hình do tiềm năng chứa hàng nghìn bản ghi.EF Competing SaveChanges() Gọi

Những gì tôi đã thực hiện để giải quyết vấn đề này được thiết lập SqlBulkCopy để xử lý hai mô hình có khả năng cao này và để EF xử lý phần còn lại của phần chèn (và tính toàn vẹn tham chiếu).

hàng loạt Loop:

foreach (var unitDetails in BatchUnits) 
{ 
    var unitOfWork = new Unit(unitDetails); 
    Task.Factory.StartNew(() => 
    { 
     unitOfWork.ProcessX(); // data preparation 
     unitOfWork.ProcessY(); // data preparation 
     unitOfWork.PersistCase(); 
    }); 
} 

Đơn vị:

class Unit 
{ 
    public PersistCase() 
    { 
    using (var dbContext = new CustomDbContext()) 
    { 
     // Need an explicit transaction so that 
     // EF + SqlBulkCopy act as a single block 
     using (var scope = new TransactionScope(TransactionScopeOption.Required, 
     new TransactionOptions() { 
      IsolationLevel = System.Transaction.IsolationLevel.ReadCommitted 
     })) 
     { 
     // Let EF Insert most of the records 
     // Note Insert is all it is doing, no update or delete 
     dbContext.Units.Add(thisUnit); 
     dbContext.SaveChanges(); // deadlocks, DbConcurrencyExceptions here 

     // Copy Auto Inc Generated Id (set by EF) to DataTables 
     // for referential integrity of SqlBulkCopy inserts 
     CopyGeneratedId(thisUnit.AutoIncrementedId, dataTables); 

     // Execute SqlBulkCopy for potentially numerous model #1 
     SqlBulkCopy bulkCopy1 = new SqlBulkCopy(...); 
     ... 
     bulkCopy1.WriteToServer(dataTables["#1"]); 

     // Execute SqlBulkCopy for potentially number model #2 
     SqlBulkCopy bulkCopy2 = new SqlBulkCopy(...); 
     ... 
     bulkCopy2.WriteToServer(dataTables["#2"]); 

     // Commit transaction 
     scope.Complete(); 
     } 
    } 
    } 
} 

Ngay bây giờ tôi đang chủ yếu bị mắc kẹt giữa một tảng đá và một nơi khó khăn. Nếu tôi rời khỏi số IsolationLevel được đặt thành ReadCommitted, tôi sẽ nhận được các sự tắc nghẽn giữa các câu hỏi EFINSERT khác nhau Tasks.

Nếu tôi đặt IsolationLevel thành ReadUncommitted (điều mà tôi nghĩ là không sao vì tôi không làm bất kỳ SELECTs), tôi nhận được DbConcurrencyExceptions.

Tôi không thể tìm thấy bất kỳ thông tin nào tốt về DbConcurrencyExceptionsEntity Framework nhưng tôi đoán rằng ReadUncommitted về cơ bản sẽ gây ra EF để nhận thông tin "hàng được chèn" không hợp lệ.

CẬP NHẬT

Dưới đây là một số thông tin cơ bản về những gì đang thực sự gây ra tôi deadlocking vấn đề trong khi làm chèn:

http://connect.microsoft.com/VisualStudio/feedback/details/562148/how-to-avoid-using-scope-identity-based-insert-commands-on-sql-server-2005

Rõ ràng vấn đề này cùng có mặt một vài năm trước, khi LINQ to SQL xuất hiện và Microsoft đã sửa nó bằng cách thay đổi cách scope_identity() được chọn. Bạn không chắc chắn lý do tại sao vị trí của họ đã thay đổi này là một vấn đề SQL Server khi cùng một vấn đề đến với Entity Framework.

+0

_competing_ hoặc _completing_? –

Trả lời

3

Vấn đề này được giải thích khá tốt ở đây: http://connect.microsoft.com/VisualStudio/feedback/details/562148/how-to-avoid-using-scope-identity-based-insert-commands-on-sql-server-2005

Về cơ bản một vấn đề EF nội bộ của mình. Tôi di chuyển mã của tôi để sử dụng LINQ to SQL và nó bây giờ hoạt động tốt (không còn không cần thiết SELECT cho giá trị danh tính).

quote liên quan so với cùng vấn đề chính xác trong LINQ to SQL mà đã được cố định:

Khi một bảng có một cột sắc, LINQ to SQL tạo ra cực kỳ SQL không hiệu quả để chèn vào một bảng như vậy. Giả sử bảng là Thứ tự và cột nhận dạng là Id. SQL được tạo ra là:

exec sp_executesql N'INSERT INTO [dbo].[Order] ([Colum1], [Column2]) VALUES (@ p0, @ p1)

SELECT [t0]. [Id] FROM [dbo]. [Đặt hàng] AS [t0] WHERE [t0]. [Id] = (SCOPE_IDENTITY())', N '@ p0 int, @ p1 int, @ p0 = 124, @ p1 = 432

Như người ta có thể thấy thay vì trả lại SCOPE_IDENTITY() trực tiếp bằng cách sử dụng ' SELECT SCOPE_IDENTITY() ', SQL được tạo ra thực hiện một SELECT trên cột Id bằng cách sử dụng giá trị được trả về bởi SCOPE_IDENTITY(). Khi số lượng các bản ghi trong bảng là lớn, điều này làm chậm đáng kể số xuống quá trình chèn. Khi bảng được phân vùng, vấn đề sẽ bị thậm chí tệ hơn.