簡體   English   中英

無法找出哪些兩個對象附​​加到不同的ObjectContext對象

[英]Cannot figure out which two objects are attached to different ObjectContext objects

因此,我最近使用了一個已有的系統(MVC /實體框架),並添加了一堆緩存存儲庫以加快速度。 它在測試中似乎工作正常,但是當我們推出它時,我們是從隨機用戶那里隨機得到此錯誤的:

Exception Message: The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects.

Stack Trace: at System.Data.Entity.Core.Objects.DataClasses.RelatedEnd.ValidateContextsAreCompatible(RelatedEnd targetRelatedEnd) at System.Data.Entity.Core.Objects.DataClasses.RelatedEnd.Add(IEntityWrapper wrappedTarget, Boolean applyConstraints, Boolean addRelationshipAsUnchanged, Boolean relationshipAlreadyExists, Boolean allowModifyingOtherEndOfRelationship, Boolean forceForeignKeyChanges) at System.Data.Entity.Core.Objects.ObjectStateManager.PerformAdd(IEntityWrapper wrappedOwner, RelatedEnd relatedEnd, IEntityWrapper entityToAdd, Boolean isForeignKeyChange) at System.Data.Entity.Core.Objects.ObjectStateManager.PerformAdd(IList`1 entries) at System.Data.Entity.Core.Objects.ObjectStateManager.DetectChanges() at System.Data.Entity.Internal.InternalContext.DetectChanges(Boolean force) at System.Data.Entity.Internal.InternalContext.GetStateEntries(Func`2 predicate) at System.Data.Entity.Infrastructure.DbChangeTracker.Entries() at System.Data.Entity.DbContext.GetValidationErrors() at System.Data.Entity.Internal.InternalContext.SaveChanges() at ClinicalCMS.Domain.CMSContext.SaveChanges() in C:\Projects\BeaconEDC\ClinicalCMS.Domain\CMSContext.cs:line 255 at BeaconEDC.Areas.DataEntry.Controllers.EngineController.MarkComplete(Int64 studyId, Int64 subjectId, Int64 formIteration, StudyAssignment assignment, String caseId, StudyForm studyForm) in C:\Projects\BeaconEDC\BeaconEDC\Areas\DataEntry\Controllers\EngineController.cs:line 375 at BeaconEDC.Areas.DataEntry.Controllers.EngineController.Edit(Int64 studyId, Int64 subjectId, Int64 formIteration, Int64 studyFormId, FormCollection collection) in C:\Projects\BeaconEDC\BeaconEDC\Areas\DataEntry\Controllers\EngineController.cs:line 312 at lambda_method(Closure , ControllerBase , Object[] ) at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters) at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c.b__9_0(IAsyncResult asyncResult, ActionInvocation innerInvokeState) at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`2.CallEndDelegate(IAsyncResult asyncResult) at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult) at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<>c__DisplayClass11_0.b__0() at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<>c__DisplayClass11_2.b__2() at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethodWithFilters(IAsyncResult asyncResult) at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass3_6.b__4() at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass3_1.b__1(IAsyncResult asyncResult)

現在,我在很多地方都寫了數據庫,但是它總是在同一個地方報告這個。 但是在那部分代碼中我沒有看到任何使用任何緩存存儲庫中的任何內容的東西。 實際上,我只是創建一個新對象,向其中添加一堆東西(可以是字符串,長整數,整數或日期),將其添加到上下文中,然后保存更改。

為了使事情更加令人沮喪,我從來沒有能夠在Visual Studio中重現它。 僅在服務器上發生。

所以我的問題是,有什么辦法可以從異常中獲取更多信息? 確切說出正在談論的兩個對象的某種方式?

這是失敗的地方:

HatterasGlobal global = repo.HatterasGlobal(iid, caseId, "FormStatus");
if (global == null)
{
    global = new HatterasGlobal();
    global.CaseID = caseId;
    global.GlobalName = "FormStatus";
    global.Mode = 1;
    global.Value = "C";
    global.Modified = DateTime.Now;
    global.HatterasInstrumentID = iid;
    global.StudyId = studyId;
    global.SubjectId = subjectId;
    global.FormIteration = formIteration;

    repo.Add(global);
}
else
{
    global.Value = "C";
    global.Modified = DateTime.Now;

    repo.SaveChanges();
}

// set the date complete
HatterasGlobal dateGlobal = repo.HatterasGlobal(studyForm.Form.IID, caseId, "DateComplete");
if (dateGlobal == null)
{
    dateGlobal = new HatterasGlobal();
    dateGlobal.CaseID = caseId;
    dateGlobal.GlobalName = "DateComplete";
    dateGlobal.Mode = 1;
    dateGlobal.Value = DateTime.Now.ToShortDateString();
    dateGlobal.Modified = DateTime.Now;
    dateGlobal.HatterasInstrumentID = iid;
    dateGlobal.StudyId = studyId;
    dateGlobal.SubjectId = subjectId;
    dateGlobal.FormIteration = formIteration;

    repo.Add(dateGlobal);
}
else
{
    dateGlobal.Value = DateTime.Now.ToShortDateString();
    dateGlobal.Modified = DateTime.Now;

    repo.SaveChanges();
}

它實際上在堆棧跟蹤中轟炸的那一行是這個家伙:

HatterasGlobal dateGlobal = repo.HatterasGlobal(studyForm.Form.IID, caseId, "DateComplete");

但是我懷疑repo.Add(global)有問題。 所有這些是這樣的:

public void Add(HatterasGlobal global)
{
    _context.HatterasGlobals.Add(global);
    _context.SaveChanges();
}

這是注冊上下文(統一)的位置:

container.RegisterType<CMSContext>(new PerRequestLifetimeManager(), new InjectionConstructor());
...
container.RegisterType<IDataEntryRepository, CachedDataEntryRepository>(new PerRequestLifetimeManager(), new InjectionConstructor(typeof(CMSContext)));

在倉庫內,上下文被初始化為:

protected CMSContext _context;
public EFDataEntryRepository(CMSContext context)
{
   this._context = context;
}

(EFDataEntryRepository是CachedDataEntryRepository的基類。)

這是我如何進行緩存的示例:

public override Study Study(long id)
        {
            var study = HttpRuntime.Cache["Study-" + id + "-" + HttpContext.Current.User.Identity.Name] as Study;
            if (study == null)
            {
                lock (CacheLockObject)
                {
                    study = HttpRuntime.Cache["Study-" + id + "-" + HttpContext.Current.User.Identity.Name] as Study;
                    if (study == null)
                    {
                        study = base.Study(id);
                        if (study != null) HttpRuntime.Cache.Insert("Study-" + id + "-" + HttpContext.Current.User.Identity.Name, study, null, _absolute, _sliding);
                    }
                }
            }

            return study;
        }

有幫助嗎?

“在ClinicalCMS.Domain.CMSContext.SaveChanges()處...”您需要進一步深入異常堆棧,使其超過“ ...”,這將成為問題的症結所在。

通常的罪魁禍首是在周圍傳遞實體的代碼。 作為一個簡單的例子:

public IEnumerable<RelatedEntity> LoadRelated()
{
    using (var context = new MyDbContext())
    {
        return context.RelatedEntities.ToList();
    }
}

public void SaveEntity(ParentEntity entity)
{
    using (var context = new MyDbContext())
    {
        context.ParentEntities.Add(entity);
        context.SaveChanges()
    }
}

public void SomeAction(ParentEntityViewModel viewModel)
{
    var relatedEntities = LoadRelated();
    var relatedEntity = relatedEntities
        .OrderBy(x => x.ApplicableDate)
        .First(x => x.ApplicableDate >= DateTime.Now);
    var newEntity = new ParentEntity
    {
        Name = viewModel.Name,
        RelatedEntity = relatedEntity
     };
     SaveEntity(newEntity);
}

……再次是一個簡單的演示示例。 這通常在代碼使用諸如存儲庫模式之類的東西時體現出來,它們要么利用不同的有界上下文(即,加載相同實體的不同DbContext),要么所討論的DbContext聲明了多個實例,例如不同的生存期范圍,具體取決於您是否使用了是否有IoC容器。

上面的示例使用了DbContext的2個不同實例。 相關實體在第一個中加載,但保存使用第二個。 當我們創建父實體並關聯仍被第一個實體跟蹤的實體,然后將其分配給第二個實體時,您將看到該錯誤。

鑒於您看到的是生產中的錯誤,但沒有測試中的錯誤,因此我懷疑以下兩種情況之一:

  1. 這是一種罕見的情況,用戶跳閘,您在測試時不會重現。
  2. 這是一個並發的會話問題,您的緩存做得很頑皮。

我傾向於#2,因為緩存和實體從來都不是真正應該做的事情。 如果您的DbContext是通過EF上下文按請求確定作用域的,並且您正在加載實體,然后將它們扔到靜態緩存中,而該請求處於活動狀態,則其他產生DbContext的請求可能會從緩存中挑選實體。 這些實體將與另一個請求的DbContext關聯,並可能導致類似您所看到的錯誤。 您所做的任何緩存都只能在會話狀態下進行,而不能是靜態/交叉會話。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM