Attempting to make persistent and update key a non transient oid

May 18, 2013 at 11:53 AM
Hi.
I'm running into the following error, and I can't quite figure out what it means.
Attempting to make persistent and update key a non transient oid
Coordinator
May 20, 2013 at 10:06 AM
It means nothing to me either! More troubling: I can't find anything like that message in the Resources file, or in the code. Please can you post the stack trace that shows where the exception is originating? Then we can hopefully explain it and make it clearer.
May 20, 2013 at 11:48 AM
Here it is

Attempting to make persistent and update key a non transient oid
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: NakedObjects.Core.Util.NakedObjectAssertException: Attempting to make persistent and update key a non transient oid

Source Error:

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace:


[NakedObjectAssertException: Attempting to make persistent and update key a non transient oid]
NakedObjects.Core.Util.Assert.AssertTrue(String message, Object target, Boolean flag) +178
NakedObjects.Core.Util.Assert.AssertTrue(String message, Boolean flag) +42
NakedObjects.EntityObjectStore.EntityOid.MakePersistentAndUpdateKey(Object[] newKey) +80
NakedObjects.EntityObjectStore.EntityObjectStore.HandleAdded(INakedObject nakedObject) +197
NakedObjects.Architecture.Util.CollectionUtils.ForEach(IEnumerable1 toIterate, Action1 action) +204
NakedObjects.EntityObjectStore.LocalContext.PostSaveWrapUp(EntityObjectStore store) +192
NakedObjects.EntityObjectStore.EntityObjectStore.<PostSaveWrapUp>b__64(LocalContext c) +39
NakedObjects.Architecture.Util.CollectionUtils.ForEach(IEnumerable1 toIterate, Action1 action) +204
NakedObjects.EntityObjectStore.EntityObjectStore.PostSaveWrapUp() +92
NakedObjects.EntityObjectStore.EntityObjectStore.EndTransaction() +604
NakedObjects.Persistor.Objectstore.ObjectStoreTransaction.Commit() +168
NakedObjects.Persistor.Objectstore.ObjectStoreTransactionManager.EndTransaction() +93
NakedObjects.Persistor.Objectstore.ObjectStorePersistor.EndTransaction() +52
NakedObjects.Web.Mvc.Controllers.NakedObjectsController.OnActionExecuted(ActionExecutedContext filterContext) +102
NakedObjects.Web.Mvc.Controllers.GenericControllerImpl.OnActionExecuted(ActionExecutedContext filterContext) +83
System.Web.Mvc.Controller.System.Web.Mvc.IActionFilter.OnActionExecuted(ActionExecutedContext filterContext) +9
System.Web.Mvc.Async.<>c__DisplayClass4f.<InvokeActionMethodFilterAsynchronously>b__49() +251
System.Web.Mvc.Async.<>c__DisplayClass37.<BeginInvokeActionMethodWithFilters>b__36(IAsyncResult asyncResult) +10
System.Web.Mvc.Async.WrappedAsyncResult1.End() +57
System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethodWithFilters(IAsyncResult asyncResult) +48
System.Web.Mvc.Async.<>c__DisplayClass2a.<BeginInvokeAction>b__20() +24
System.Web.Mvc.Async.<>c__DisplayClass25.<BeginInvokeAction>b__22(IAsyncResult asyncResult) +102
System.Web.Mvc.Async.WrappedAsyncResult
1.End() +57
System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction(IAsyncResult asyncResult) +43
System.Web.Mvc.<>c__DisplayClass1d.<BeginExecuteCore>b__18(IAsyncResult asyncResult) +14
System.Web.Mvc.Async.<>c__DisplayClass4.<MakeVoidDelegate>b__3(IAsyncResult ar) +23
System.Web.Mvc.Async.WrappedAsyncResult1.End() +62
System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +57
System.Web.Mvc.Async.<>c__DisplayClass4.<MakeVoidDelegate>b__3(IAsyncResult ar) +23
System.Web.Mvc.Async.WrappedAsyncResult
1.End() +62
System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult) +47
System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(IAsyncResult asyncResult) +10
System.Web.Mvc.<>c__DisplayClass8.<BeginProcessRequest>b__3(IAsyncResult asyncResult) +25
System.Web.Mvc.Async.<>c__DisplayClass4.<MakeVoidDelegate>b__3(IAsyncResult ar) +23
System.Web.Mvc.Async.WrappedAsyncResult`1.End() +62
System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +47
System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +9
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +9629296
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155
Coordinator
May 20, 2013 at 12:06 PM
Thanks. (Not sure why my code search didn't locate the message, as it is clearly there!).

The message is essentially the same as the somewhat clearer version in an adjacent method: "Attempting to make persistent a non transient oid"

It essentially means that you are calling Container.Persist() on an object that is not transient i.e. has already been persisted - either directly using Persist(), or indirectly, because the object was associated with another object that was explicitly persisted.

I will raise a ticket to make those messages clearer.
May 20, 2013 at 1:33 PM
Edited May 20, 2013 at 1:37 PM
I am very confused.
I have a ReplacementRequest that has a VehicleDescriptionId in the DB (so a navigation property to VehicleDescription)
I have a method that is trying to create both a ReplacementRequest and a VehicleDescription, and associate the latter to the former and then persist it all, but I keep getting that error.

I have tried various things. Both of the following approaches throw the same exception:
            var vehicle = this.Container.NewTransientInstance<VehicleDescription>();
            var request = this.Container.NewTransientInstance<ReplacementRequest>();
            request.VehicleDescription = vehicle;
            this.Container.Persist(ref request);
            var vehicle = this.Container.NewTransientInstance<VehicleDescription>();
            this.Container.Persist(ref vehicle);
            var request = this.Container.NewTransientInstance<ReplacementRequest>();
            this.Container.Persist(ref request);
            request.VehicleDescription = vehicle;
I have done this kind of thing many times before, and hence don't understand what is going on here.
VehicleDescriptionId is null, so if I do not persist and associate a VehicleDescription, it saves fine.

Even if I do the following, I get the same error, which makes no sense.
            var vehicle = this.Container.NewTransientInstance<VehicleDescription>();
            this.Container.Persist(ref vehicle);
            var request = this.Container.NewTransientInstance<ReplacementRequest>();
            this.Container.Persist(ref request);
            //request.VehicleDescription = vehicle;
Coordinator
May 20, 2013 at 1:51 PM
Edited May 20, 2013 at 2:02 PM
I agree that there appears to be nothing wrong with what you are doing - from the part of the code shown. Suugestions:

1.To aid in diagnosis, put a break point on that line of code in the NOF source that is throwing the error and see which of the two objects it is that is deemed to be already persisted, and what its Id is at that point.

2.Is there anything unusual in the way that the Ids are being created for either of these two objects in the database (compared to how you've done it before?).

3.Do either of the objects have any implemented lifecycle methods? If so, put a break on all of them to see which, if any, are being called.
  1. Picking up on your last use case - you say you are getting the error even when you aren't associated them? In which case, can you confirm that you can create and persist each of the two types OK in isolation.
May 20, 2013 at 2:22 PM
Picking up on your last use case - you say you are getting the error even when you aren't associated them? In which case, can you confirm that you can create and persist each of the two types OK in isolation.
This is exactly what I went ahead and did and found VehicleDescription would give the error on it's own.
I tracked it down to an overloaded GetHashCode and Equals on VehicleDescription. When I commented those out, the error went away.
I don't understand this yet, however, as that code was not contained in the stack trace and even in debug mode it does not break there.
What does NOF do / expect with those 2 methods?
Coordinator
May 20, 2013 at 2:41 PM
Edited May 20, 2013 at 2:48 PM
GetHashCode and Equals are both concerned with the fundamental identity of an object, and overriding (did you mean that, rather than'overloaded'?) them can certainly lead to issues that are very hard to identify. This not just specific to Naked Objects - it can apply in other uses of EF.

See, for example, https://nakedobjects.codeplex.com/discussions/434226

I can't tell you exactly what has happened, but these methods may well be called in the persistence process (from deep in the bowels of .NET or EF code - not NOF), and an answer that is different from the default one may lead EF and/or Naked Objects to think that two different objects are the same - or vice versa.

P.S. If you search web for 'entity framework override gethashcode' [or 'equals'] you'll find that lots of people have run into this issue, and most of them have taken a long while to diagnose it. Not that that is any encouragement!
May 20, 2013 at 3:47 PM
Yes. I had a day from hell. :)
And yes, I meant overridden, not overloaded.