2012-08-25 6 views
19

Tôi nhận được "Không thể sử dụng ngữ cảnh trong khi tạo mô hình". vấn đề trong ứng dụng web của tôi ở một trong các trang web của tôi. Trang web cụ thể này POSTs tới máy chủ sau mỗi 2-3 giây để làm mới màn hình. Từ thử nghiệm của tôi, tôi thấy rằng nếu tôi có từ 2 trường hợp trình duyệt trở lên mở trang này, sau vài phút, tôi nhận được "Không thể sử dụng ngữ cảnh trong khi mô hình đang được tạo" ngoại trừ từ sâu trong kho lưu trữ.EF - Không thể sử dụng ngữ cảnh trong khi mô hình đang được tạo ngoại lệ trong các yêu cầu HTTP

Mã này gọi là "dịch vụ" để truy xuất dữ liệu cần thiết. Mã này được thực hiện trong một thuộc tính ủy quyền tùy chỉnh của lớp MVC Controller.

// Code in custom "Authorization" attribute on the controller 
int? stationId = stationCookieValue; // Read value from cookie 
RoomStationModel roomStationModel = RoomStationService.GetRoomStation(stationId); // Error occurs inside this call 

Đây là "RoomStationModel"

public class RoomStationModel 
{ 
    [Key] 
    public int RoomStationId { get; set; } 

    public int? RoomId { get; set; } 
    [ForeignKey("RoomId")] 
    public virtual RoomModel Room { get; set; } 
    /* Some other data properties.... */ 
} 

public class RoomModel 
{ 
    [Key] 
    public int RoomId { get; set; } 

    public virtual ICollection<RoomStationModel> Stations { get; set; } 
} 

Đây là mã cho các cuộc gọi dịch vụ trên:

public RoomStationModel GetRoomStation(int? roomStationId) 
{ 
    RoomStationModel roomStationModel = null; 
    if (roomStationId.HasValue) 
    { 
     using (IRepository<RoomStationModel> roomStationRepo = new Repository<RoomStationModel>(Context)) 
     { 
      roomStationModel = roomStationRepo.FirstOrDefault(rs => rs.RoomStationId == roomStationId.Value, false, new string[] { "Room" }); 
     } 
    } 

    return roomStationModel; 
} 

Đây là kho .... nơi lỗi xảy ra

public class Repository<TObject> : IRepository<TObject> where TObject : class 
    { 
     protected MyContext Context = null; 

     public Repository(IDataContext context) 
     { 
      Context = context as MyContext; 
     } 

     protected DbSet<TObject> DbSet { get { return Context.Set<TObject>(); } } 

    public virtual TObject FirstOrDefault(Expression<Func<TObject, bool>> predicate, bool track = true, string[] children = null) 
    { 
     var objectSet = DbSet.AsQueryable(); 

     if (children != null) 
      foreach (string child in children) 
       objectSet = objectSet.Include(child); 

     if (track) 
      return objectSet.Where(predicate).FirstOrDefault<TObject>(predicate); 

     return objectSet.Where(predicate).AsNoTracking().FirstOrDefault<TObject>(predicate); 
    } 
} 

Ảnh chụp màn hình lỗi: Screenshot of error occurring

stacktrace:

at System.Data.Entity.Internal.LazyInternalContext.InitializeContext() 
    at System.Data.Entity.Internal.InternalContext.Initialize() 
    at System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type entityType) 
    at System.Data.Entity.Internal.Linq.InternalSet`1.Initialize() 
    at System.Data.Entity.Internal.Linq.InternalSet`1.Include(String path) 
    at System.Data.Entity.Infrastructure.DbQuery`1.Include(String path) 
    at System.Data.Entity.DbExtensions.Include[T](IQueryable`1 source, String path) 
    at Vanguard.AssetManager.Data.Repository`1.FirstOrDefault(Expression`1 predicate, Boolean track, String[] children) in C:\Work\VanguardAssetManager\Main\Vanguard.AssetManager.Data\Repository.cs:line 100 
    at Vanguard.AssetManager.Services.Business.RoomStationService.GetRoomStation(Nullable`1 roomStationId) in C:\Work\VanguardAssetManager\Main\Vanguard.AssetManager.Services\Business\RoomStationService.cs:line 61 
    at Vanguard.AssetManager.Web.Attributes.RoomStationAuthorizeAttribute.OnAuthorization(AuthorizationContext filterContext) in C:\Work\VanguardAssetManager\Main\Vanguard.AssetManager.Web\Attributes\RoomStationAuthorizeAttribute.cs:line 52 
    at System.Web.Mvc.ControllerActionInvoker.InvokeAuthorizationFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor) 
    at System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) 

EF Version: 4.1 (Mã đầu tiên)

+0

Điều này không nên xảy ra. Mã của bạn đang làm một cái gì đó thực sự xấu bởi vì thông thường mô hình được tạo ra chỉ một lần khi bối cảnh được sử dụng lần đầu tiên. Bạn có chắc chắn rằng ứng dụng của bạn không tái chế hồ bơi ứng dụng sau mỗi yêu cầu? –

+0

Tôi không tin đó là, làm sao tôi biết được liệu nó có tái chế hồ bơi ứng dụng sau mỗi lần làm mới không? Đó có phải là một điều IIS hoặc một nơi nào đó trong mã? – contactmatt

+0

Một điều mà tôi thấy thú vị là lỗi này chỉ xảy ra khi tôi sử dụng thuộc tính ủy quyền tùy chỉnh trên bộ điều khiển của mình. Khi tôi xóa ủy quyền tùy chỉnh, lỗi sẽ biến mất. – contactmatt

Trả lời

0

Điều này có vẻ như là một trong hai điều, một điều kiện chủng tộc của một số loại hoặc một vấn đề "bối cảnh Phạm vi" . Bạn nên đảm bảo rằng ngữ cảnh đang được khởi tạo trong một chuỗi an toàn và rằng ngữ cảnh không được truy cập bởi các luồng khác nhau để ngăn chặn các điều kiện chủng tộc. Một nguyên nhân khó nắm bắt của lỗi này cũng chính là việc truy cập vào chính mô hình đó trong ghi đè OnModelCreation.

30

kho của bạn là ngắn ngủi (bạn tạo ra nó cho mỗi cuộc gọi đến GetRoomStation() nhưng bối cảnh thực tế của bạn dường như là tồn tại lâu dài (RoomServiceStation.Context tài sản). Điều này có nghĩa rằng tất cả các cuộc gọi đến ứng dụng web của bạn sẽ sử dụng bối cảnh tương tự

Đây là trường hợp "EF trong N-tier", nơi bạn đang cố gắng giữ một cái gì đó có trạng thái xung quanh (ngữ cảnh) trong mô hình kiến ​​trúc không trạng thái của một ứng dụng web. ngữ cảnh về các chủ đề khác nhau và bạn đang nhận được một điều kiện đua.

Một chủ đề có thể khởi động lần đầu tiên trong ngữ cảnh của bạn nse cho một yêu cầu, và người khác đến trong cố gắng sử dụng bối cảnh. Yêu cầu thứ hai cho rằng ngữ cảnh đã sẵn sàng để sử dụng và bạn nhận được ngoại lệ này. Bạn thậm chí có thể nhận được điều này nếu bạn có nhiều ngữ cảnh cố gắng "quay lên" cùng lúc với đề xuất in another SO thread.

Bạn có thể thực hiện một số việc. Bạn có thể thử khóa bi quan xung quanh truy cập vào bối cảnh của bạn, nhưng bạn đang đặt trong một nút cổ chai không cần thiết. Bạn có thể thử tạo ra một số loại "trước khi khách hàng gọi cho tôi, khởi tạo ngữ cảnh" mã, nhưng bạn phải tìm một nơi tốt để làm điều này, có lẽ sử dụng phương pháp "brute force" suggested in an MSDN thread.

Điều tốt hơn cần làm là chỉ cần tạo ngữ cảnh mới cho mọi yêu cầu đối với dịch vụ back-end của bạn. Có một số chi phí, có, nhưng tối thiểu.Chi phí trên có lẽ ít có khả năng tiêu diệt hiệu suất hơn là khóa bi quan, và sẽ không chịu sự kiện tái chế hồ bơi ứng dụng mở rộng ứng dụng web của bạn trên một trang trại và cứ thế.

Nếu bạn đang dựa vào theo dõi thay đổi hoặc tính chất nhà nước khác của một ngữ cảnh, bạn sẽ mất lợi ích này. Trong trường hợp này, bạn sẽ phải tìm ra một cơ chế khác để theo dõi và giảm thiểu số lần truy cập cơ sở dữ liệu.

Từ một MSDN article này được tóm tắt (tôi nhấn mạnh):

If you serialize entities from one tier to another, the recommended pattern is to keep the context around on the mid-tier only long enough for a single service method call. Subsequent calls will spin up a new instance of the context to complete each task.

A thread on EF/WCF/N-tier may also give you some insights, và Jorge của blog post #5 nói về EF tại N-tiers (toàn bộ loạt có thể là một đọc tốt). Và bằng cách này, tôi đã chạy vào cùng một điều chính xác: nhiều khách hàng nhấn bối cảnh cùng một lúc, dẫn đến vấn đề này.

+0

Tôi đồng ý. Tôi muốn đề nghị xem xét tiêm phụ thuộc để giải quyết vấn đề này. Gần đây tôi đã sử dụng Ninject (http://www.ninject.org/) với kết quả tuyệt vời cho các dự án MVC. – Gromer

+3

Tôi đã sử dụng một ContainerControlledLifetimeManager khi đăng ký bối cảnh của tôi với Unity. Tôi đã thay đổi nó thành một PerThreadLifetimeManager và giải quyết lỗi này. – oldegreyg

+0

@ezycheez: trong trường hợp của tôi, tôi đã sử dụng HierarchicalLifetimeManager để đăng ký ngữ cảnh của mình. Nó gây ra cùng một vấn đề. Tôi thấy vấn đề thứ hai với HierarchicalLifetimeManager và ContainerControlledLifetimeManager là nếu tôi sửa đổi thủ công dữ liệu cơ sở dữ liệu phụ trợ. Sau đó, thay đổi đó không được phản ánh trong ứng dụng web giao diện người dùng của tôi. Đó là vì đối với HierarchicalLifetimeManager và đối với ContainerControlledLifetimeManager, Unity trả về cùng một cá thể (hoặc singleton lifetime) của kiểu hoặc đối tượng đã đăng ký. Tôi thay vì sử dụng PerThreadLifetimeManager để tránh 2 vấn đề; chúng là khóa và dữ liệu cũ. –

1

Tôi gặp phải lỗi này và đã xuất hiện để giải quyết nó bằng cách cung cấp ghi đè lên phương thức Dispose() trong bộ điều khiển. Nó sẽ xuất hiện mà lực lượng đóng kết nối cơ sở dữ liệu trước khi cố gắng để mở một cái mới subverts lỗi này.

protected override void Dispose(bool disposing) 
{ 
    if(disposing) 
    { 
     _fooRepository.Dispose(); 
    } 
    base.Dispose(disposing); 
} 
0

Tôi đã gặp phải sự cố này ngày hôm nay. Vấn đề là tôi đã vô tình sử dụng cùng một trường hợp DbContext của tôi trên các yêu cầu. Yêu cầu đầu tiên sẽ tạo ra cá thể và bắt đầu xây dựng mô hình, và yêu cầu thứ hai sẽ đến và cố gắng lấy dữ liệu trong khi nó vẫn đang xây dựng.

Lỗi lầm của tôi là ngớ ngẩn. Tôi vô tình sử dụng HttpContext.Current.Cache thay vì HttpContext.Current.Items :)