2012-08-25 20 views
19

"İçerik, model oluşturulurken kullanılamaz." web sayfamda web sayfamdan birinde sorun. Bu belirli web sayfası, ekranı yenilemek için her 2-3 saniyede bir sunucuya POST yapar. Testlerimden, bu sayfaya açılan 2 veya daha fazla tarayıcı örneğim varsa, birkaç dakika geçtikten sonra "İçerik oluşturulduğunda içerik kullanılamaz" istisnasının, depodaki derinlikten istisna olduğunu fark ettim.EF - HTTP istekleri sırasında model oluşturulduğu sırada içerik kullanılamıyor

Bu kod, gerekli verileri almak için bir "hizmet" çağırır. Bu kod, MVC Denetleyici sınıfının özel bir yetkilendirme özniteliğinde yürütülür. İşte

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; 
} 

depo var .... hata

oluşur: Burada
// 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 

İşte
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; } 
} 

yukarıdaki hizmet çağrısı kodudur "RoomStationModel" dir
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); 
    } 
} 

Hatayı ekran görüntüsü: 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 Versiyon: 4.1 (Kod önce)

Bu görünüyor
+0

yerine HttpContext.Current.Cache kullanıldı. Kodunuz gerçekten kötü bir şey yapıyor çünkü normalde model, içerik ilk kez kullanıldığında sadece bir kez oluşturuluyor. Uygulamanızın her istek sonrası uygulama havuzunu geri dönüştürmediğinden emin misiniz? –

+0

Buna inanmıyorum, her bir yenilemeden sonra uygulama havuzunu geri dönüştürüp dönüştürmediğini nasıl anlarım? Bu bir IIS meselesi mi yoksa kodun içinde bir yer mi? – contactmatt

+0

Bulduğum bir şey ilginçti, bu hata yalnızca denetleyicimde özel yetkilendirme özelliğini kullandığımda ortaya çıkıyor. Özel yetkilendirmeyi kaldırdığımda hata gider. – contactmatt

cevap

0

iki şeyden birinin, bazı tür ya da bir "bağlam kapsam" konunun bir yarış koşulu olarak . Bağlamın bir iş parçacığı güvenli bir şekilde başlatıldığından ve koşulların engellenmesi için içeriğin farklı iş parçacıkları tarafından erişilmediğinden emin olmalısınız. Bu hatanın nedenini yakalamak zorsa, modelin OnModelCreation geçersiz kılmada da erişilmesidir.

30

Deponuz kısa sürelidir (her çağrı için GetRoomStation()'a göre oluşturuyorsunuz ancak gerçek içeriğiniz uzun ömürlü görünüyor (RoomServiceStation.Context özelliği). Bu, web uygulamanıza yapılan her çağrının aynı içeriği kullanacağı anlamına gelir.

Bu, bir web uygulamasının mimari durumdaki vatansız modelinde bir durumun çevresinde (bağlam) durumunuzu sürdürmeye çalıştığınız "N katmandaki EF" senaryosudur.Tüm bu talepler aynı şekilde kanalize edilir. Farklı iş parçacıkları bağlamında ve bir yarış durumu alıyorsunuz

Bir iş parçacığı, içeriğinizin ilk kez başlatılmasını yanıt olarak başlatıyor olabilir Bir istek için nse, ve başka bir bağlam kullanmayı denemeye gelir. İkinci istek, içeriğin kullanıma hazır olduğunu düşünür ve bu istisnayı alırsınız. "0," ile aynı anda "spin up" yapmaya çalışan birden çok bağlam varsa bunu bile alabilirsiniz.

Birkaç şey yapabilirsiniz. Bağlantınıza erişim etrafında karamsar bir şekilde durmayı deneyebilirsiniz, ancak gereksiz bir darboğaz getiriyorsunuz. "İstemciler beni çağırmadan önce, bağlamı başlat" kodunun bir türünü oluşturmayı deneyebilirsiniz, ancak belki de "kaba kuvvet" yöntemi suggested in an MSDN thread'u kullanarak bunu yapmak için iyi bir yer bulmalısınız.

Yapmanız gereken daha iyi bir şey, arka uç hizmetinizin her isteği için için yeni bir içerik oluşturmaktır. Biraz yük var, evet, ama minimal.Ek yükün, kötümser kilitlemeden daha fazla performans gösterme olasılığı daha düşüktür ve web uygulamanızı bir çiftlikte ve diğer yerlerde ölçekleyen uygulama havuzu geri dönüşüm etkinliklerine tabi olmayacaktır.

Değişikliğin izlenmesi veya içeriğin diğer durumsal yapısına güveniyorsanız, bu avantajı kaybedersiniz. Bu durumda, veritabanı isabetlerini izlemek ve en aza indirmek için farklı bir mekanizma kurmak zorunda kalacaksınız. Bu özetlenebilir bir MSDN article (vurgu benim) Gönderen

: N-Tiers EF hakkında

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 ve Jorge blog post #5 görüşmeler (tüm serisi iyi bir okuma olabilir). Ve bu arada, tam olarak aynı şeye girdim: birçok müşteri aynı anda bağlama çarparak, bu konuyla sonuçlanıyor.

+0

Katılıyorum. Bunu çözmek için bağımlılık enjeksiyonuna bakmanızı öneriyorum. Son zamanlarda MVC projeleri için harika sonuçlar ile Ninject (http://www.ninject.org/) kullandım. – Gromer

+3

Birliğimi Unity ile kaydederken ContainerControlledLifetimeManager kullanıyordum. Onu bir PerThreadLifetimeManager olarak değiştirdim ve bu hatayı çözdüm. – oldegreyg

+0

@ezycheez: Benim durumumda, içeriğimi kaydetmek için HierarchicalLifetimeManager'ı kullandım. Aynı soruna neden oldu. Arka uç veritabanı verilerini el ile değiştirdiğimde HierarchicalLifetimeManager ve ContainerControlledLifetimeManager ile ikinci sorunu buldum. Sonra bu değişiklik ön uç web uygulamasında yansıtılmadı. Bunun nedeni, HierarchicalLifetimeManager için ve ContainerControlledLifetimeManager için olduğu gibi, Unity, kayıtlı tür veya nesnenin aynı örneğini (veya tekil yaşam süresini) döndürür. Bunun yerine 2 sorunu önlemek için PerThreadLifetimeManager kullanıyorum; Onlar kilit ve veri bayat. –

1

Bu hatayla karşılaştım ve denetleyicideki Dispose() yöntemine bir geçersiz kılma sağlayarak çözdünüz. Yeni bir tane açmaya çalışmadan önce veritabanı bağlantısını kapatmanın zorluğunun bu hatayı altüst ettiği görülür.

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

Bu sorunu bugün yaşadım. Sorun, yanlışlıkla istemlerimin DbContext'inin aynı örneğini kullanmamdı. İlk istek örneği oluşturacak ve modeli oluşturmaya başlayacak ve ikinci istek gelip hala oluştururken veri almaya çalışacaktı.

Benim hatam aptaldı. Yanlışlıkla HttpContext.Current.Items :)