2010-07-16 36 views
28

Làm quen với Khung thực thể, tôi thực sự khá mắc kẹt về cách tiếp tục với bộ vấn đề này. Về dự án tôi hiện đang làm việc, toàn bộ trang web được tích hợp rất nhiều với mô hình EF. Đầu tiên, việc truy cập vào bối cảnh EF đã được kiểm soát bằng cách sử dụng bootstrapper Dependency Injection. Vì lý do hoạt động, chúng tôi không thể sử dụng thư viện DI. Tôi đã loại bỏ điều này và sử dụng một mô hình cá thể của đối tượng ngữ cảnh khi được yêu cầu. Tôi bắt đầu nhận được ngoại lệ sau:Khuôn khổ và giao dịch .NET Entity

Loại 'XXX' đã được ánh xạ nhiều lần.

Chúng tôi đi đến kết luận rằng các trường hợp khác nhau của bối cảnh đã gây ra vấn đề này. Sau đó tôi đã trừu tượng đối tượng ngữ cảnh thành một cá thể tĩnh đơn lẻ đang được truy cập bởi mỗi luồng/trang. Tôi hiện đang nhận được một trong một số ngoại lệ về giao dịch:

Giao dịch mới không được phép vì có các chủ đề khác đang chạy trong phiên.

Không thể thực hiện thao tác giao dịch vì có yêu cầu đang chờ xử lý làm việc trên giao dịch này.

ExecuteReader yêu cầu lệnh để có giao dịch khi kết nối được gán cho lệnh nằm trong giao dịch địa phương đang chờ xử lý. Thuộc tính Giao dịch của lệnh chưa được khởi tạo.

Trường hợp ngoại lệ cuối cùng xảy ra trong quá trình tải. Tôi đã không cố gắng để lưu trạng thái bối cảnh trở lại Db trên thread mà không thành công. Có một thread khác thực hiện một hoạt động như vậy tuy nhiên.

Những ngoại lệ này không liên tục ở mức tốt nhất, nhưng tôi đã quản lý để đưa trang web vào trạng thái nơi các kết nối mới bị từ chối do khóa giao dịch. Rất tiếc, tôi không thể tìm thấy chi tiết ngoại lệ.

Tôi đoán câu hỏi đầu tiên của mình là, liệu mô hình EF có được sử dụng từ một cá thể đơn lẻ không? Ngoài ra, có thể loại bỏ nhu cầu giao dịch trong EF không? Tôi đã thử sử dụng một đối tượng TransactionScope mà không thành công ...

Thành thật mà nói, tôi rất nhiều khó khăn ở đây, và không thể hiểu tại sao (những gì phải là) hoạt động đơn giản đang gây ra vấn đề như vậy ...

+0

Liên quan: http://stackoverflow.com/questions/10585478/one-dbcontext-per-web-request-why – Steven

+0

Quá xấu bạn không thể sử dụng bootstrapper IOC, vì giải pháp có [Ninject] (http : //www.ninject.org/) sẽ liên kết một cá thể "phổ biến" với phạm vi _request_, như những người khác đã gợi ý: 'kernel.Bind >(). > () .InRequestScope(); '- phần quan trọng là **' InRequestScope' ** – drzaus

Trả lời

50

Tạo toàn cầu ObjectContext trong ứng dụng web rất tệ. Lớp ObjectContext không an toàn. Nó được xây dựng xung quanh khái niệm của unit of work và điều này có nghĩa là bạn sử dụng nó để vận hành một trường hợp sử dụng duy nhất: do đó cho một giao dịch kinh doanh. Nó có nghĩa là để xử lý một yêu cầu duy nhất.

Trường hợp ngoại lệ bạn nhận được xảy ra vì đối với mỗi yêu cầu bạn tạo giao dịch mới, nhưng hãy thử sử dụng cùng một số ObjectContext. Bạn may mắn rằng các ObjectContext phát hiện này và ném một ngoại lệ, bởi vì bây giờ bạn phát hiện ra rằng điều này sẽ không hoạt động.

Hãy suy nghĩ về lý do tại sao điều này không thể hoạt động. ObjectContext chứa bộ nhớ cache cục bộ của các thực thể trong cơ sở dữ liệu của bạn. Nó cho phép bạn thực hiện một loạt các thay đổi và cuối cùng gửi những thay đổi đó đến cơ sở dữ liệu. Khi sử dụng một đơn lẻ ObjectContext, với nhiều người dùng gọi SaveChanges trên đối tượng đó, làm thế nào là nó phải biết chính xác những gì nên được cam kết và những gì không nên?Bởi vì nó không biết, nó sẽ lưu tất cả các thay đổi, nhưng tại thời điểm đó người dùng khác có thể vẫn đang thực hiện thay đổi. Khi bạn may mắn hoặc EF hoặc cơ sở dữ liệu của bạn sẽ thất bại, bởi vì các thực thể đang ở trạng thái không hợp lệ. Nếu bạn không may đối tượng đang ở trong một trạng thái không hợp lệ được lưu thành công vào cơ sở dữ liệu và bạn có thể tìm thấy trong vài tuần sau đó cơ sở dữ liệu của bạn đầy những crap. Giải pháp cho vấn đề của bạn là create at least one ObjectContext per request. Mặc dù trong lý thuyết bạn có thể lưu trữ một ngữ cảnh đối tượng trong phiên người dùng, đây cũng là một ý tưởng tồi, vì ObjectContext thường sẽ quá dài và sẽ chứa dữ liệu cũ (vì bộ đệm trong của nó sẽ không tự động được làm mới).

CẬP NHẬT:

Cũng lưu ý rằng có một ObjectContext mỗi thread phải là xấu như có một trường hợp duy nhất cho các ứng dụng web hoàn chỉnh. ASP.NET sử dụng một hồ bơi thread có nghĩa là một số lượng hạn chế của các chủ đề sẽ được tạo ra trong suốt cuộc đời của một ứng dụng web. Điều này về cơ bản có nghĩa là những trường hợp ObjectContext sẽ trong trường hợp đó vẫn còn sống trong suốt thời gian của ứng dụng, gây ra cùng một vấn đề với độ dữ liệu. Bạn có thể nghĩ rằng có một DbContext cho mỗi luồng thực sự là an toàn thread, nhưng đây không phải là trường hợp, vì ASP.NET có một mô hình không đồng bộ cho phép hoàn thành các yêu cầu trên một luồng khác với nơi nó được khởi động (và các phiên bản mới nhất của MVC và Web API thậm chí cho phép một số lượng tùy ý các luồng xử lý một yêu cầu duy nhất theo thứ tự tuần tự). Điều này có nghĩa là chuỗi bắt đầu một yêu cầu và được tạo ra ObjectContext có thể có sẵn để xử lý một yêu cầu khác trước khi yêu cầu ban đầu đó kết thúc. Tuy nhiên, các đối tượng được sử dụng trong yêu cầu đó (chẳng hạn như trang web, bộ điều khiển hoặc bất kỳ lớp nghiệp vụ nào), vẫn có thể tham chiếu đến ObjectContext. Vì yêu cầu web mới chạy trong cùng một chuỗi đó, nó sẽ nhận được cùng một ví dụ ObjectContext như yêu cầu cũ đang sử dụng. Điều này một lần nữa gây ra các điều kiện chủng tộc trong ứng dụng của bạn và gây ra các vấn đề an toàn chủ đề giống như những gì một trong những nguyên nhân của trường hợp toàn cầu ObjectContext.

+1

Vâng, thực sự đó là những gì tôi nghĩ tôi đang làm ... Tôi sẽ không bao giờ thường sử dụng các đối tượng bối cảnh dữ liệu tĩnh, nhưng đã trở nên tuyệt vọng. Hóa ra tôi đã có lỗi A N khác, nơi tôi đã có một trường hợp duy nhất của bối cảnh.Tuy nhiên, họ đã ở trong một vật thể tĩnh, mà dường như là nguyên nhân. 2 giờ cuộc sống của tôi không quay lại ... Cảm ơn – sicknote

+19

Chỉ hai giờ? Sau đó, bạn là một người may mắn :-) – Steven

+1

Và lựa chọn nào tốt nhất để đối mặt với loại vấn đề này? – Romias

4

Khi bạn tham khảo "trang web" trong câu hỏi của bạn, tôi giả định đây là một ứng dụng web. Các thành viên tĩnh tồn tại chỉ một lần cho toàn bộ ứng dụng, nếu bạn đang sử dụng một kiểu mẫu đơn với một cá thể ngữ cảnh duy nhất trên toàn bộ ứng dụng, tất cả các loại yêu cầu sẽ có trong tất cả các trạng thái trên toàn bộ ứng dụng.

Một trường hợp ngữ cảnh tĩnh đơn lẻ sẽ không hoạt động, nhưng nhiều trường hợp ngữ cảnh cho mỗi luồng sẽ phiền hà cũng như bạn không thể kết hợp và kết hợp ngữ cảnh. Những gì bạn cần là một ngữ cảnh duy nhất cho mỗi chủ đề. Chúng tôi đã thực hiện điều này trong ứng dụng của mình bằng cách sử dụng mẫu kiểu chèn phụ thuộc. BLL và DAL lớp của chúng tôi tham gia một bối cảnh như một tham số trong các phương pháp, cách mà bạn có thể làm điều gì đó như dưới đây:

using (TransactionScope ts = new TransactionScope()) 
{ 
    using (ObjectContext oContext = new ObjectContext("MyConnection")) 
    { 
     oBLLClass.Update(oEntity, oContext); 
    } 
} 

Nếu bạn cần phải gọi phương pháp BLL/Dal khác trong bản cập nhật của bạn (hoặc bất kỳ phương pháp nào bạn chọn) bạn chỉ cần vượt qua cùng một bối cảnh xung quanh. Theo cách đó, các cập nhật/chèn/xóa là nguyên tử, eveything trong một phương thức duy nhất là sử dụng cùng một cá thể ngữ cảnh, nhưng cá thể đó không được sử dụng bởi các luồng khác.