This project is read-only.

InvalidOperationException persisting many to many

Feb 8, 2012 at 2:53 PM

I have the following 3 types:  Application, Role and ApplicationRole.

ApplicationRole is a mapping of an application to a role.  An application can map to many roles, and a role can map to many applications.

I am try to create and persist an instance of each of these types programmatically within an ObjectPersistor transaction.  I.e. NakedObjectsContext.ObjectPersistor.StartTransaction/EndTransaction.

I first create an application instance and persist it in it's own transaction. This succeeds.

I then create a role instance and persist it in it's own transaction. This succeeds.

I then create and persist an applicationrole instance in it's own transaction, referencing the application and role instances that were previously committed.

This fails with the following error:

"The object is in a detached state. This operation cannot be performed on an ObjectStateEntry when the object is detached."

What am I missing or doing wrong?

Feb 8, 2012 at 3:11 PM

Paul

1.  Please clarify:  are you doing this from the Naked Objects UI or not?  If not please remnind us of that each time you raise a query  -  because otherwise we spend a lot of time chasing down the wrong avenue  -  and it might mislead other people also. 

2.  If all you want to do is have a many-to-many relationship between an Application and a Role  -  can you not just delegate this to Entity Framework.  If you specify a many-to-many relationship in the entity model then EF will create an association table (that corresponds to your ApplicationRole object) but is never surfaced as an entity or an object.  That's not to say there's never a reason to do it the way that you have (e.g. if the mapping itself needs to have additional data associated with it such as start & end dates, or if the numbers involved are too large to represent as direct collections in the UI)  -  but I'd like to know if you did have a good reason for representing ApplicationRole as an explicit object.

3.  In your transaction that creates the ApplicationRole, how are you getting hold of the Application and the Role?  Are you retrieving them afresh from the container, or have you held on to references to them from within the transactions you created them in.  If the latter, is it possible that you are holding onto the pre-persisted version of the object.  N.B.  the reason why the Persist method syntax is Perist(ref obj) is that the object itself is swapped when you persist it.  One way to test this is to assert, just before adding them in to the ApplicationRole object that the Application and the Role objects are in fact persisted at that point (by calling Container.IsPersistent(...) ).

Hope those pointers help.

Richard

Feb 8, 2012 at 3:34 PM

Hi Richard,

Thank you for getting back to me so quickly.  I will try and answer each of your questions below:

Q1.  Please clarify:  are you doing this from the Naked Objects UI or not?  If not please remnind us of that each time you raise a query  -  because otherwise we spend a lot of time chasing down the wrong avenue  -  and it might mislead other people also. 

A1.  My statement that "I am try to create and persist an instance of each of these types programmatically within an ObjectPersistor transaction.  I.e. NakedObjectsContext.ObjectPersistor.StartTransaction/EndTransaction." is probably ambiguous,  so I will clarify;  

I am not doing this from the NakedObjects UI.  Note: I will try and always add this statement in future. Sorry for any time you have wasted.

Q2.  If all you want to do is have a many-to-many relationship between an Application and a Role  -  can you not just delegate this to Entity Framework.  If you specify a many-to-many relationship in the entity model then EF will create an association table (that corresponds to your ApplicationRole object) but is never surfaced as an entity or an object.  That's not to say there's never a reason to do it the way that you have (e.g. if the mapping itself needs to have additional data associated with it such as start & end dates, or if the numbers involved are too large to represent as direct collections in the UI)  -  but I'd like to know if you did have a good reason for representing ApplicationRole as an explicit object.

A2. Our ApplicationRole type needs to have additional data associated with it, so I do have a good reason for representing the many to many relationship as an explicit object.

Q3.  In your transaction that creates the ApplicationRole, how are you getting hold of the Application and the Role?  Are you retrieving them afresh from the container, or have you held on to references to them from within the transactions you created them in.  If the latter, is it possible that you are holding onto the pre-persisted version of the object.  N.B.  the reason why the Persist method syntax is Perist(ref obj) is that the object itself is swapped when you persist it.  One way to test this is to assert, just before adding them in to the ApplicationRole object that the Application and the Role objects are in fact persisted at that point (by calling Container.IsPersistent(...) ).

A3. I retrieve both the Application and the Role references by using a LINQ query that selects them afresh from Container.Instances<T>().  I have also checked that the Application and Role objects are correctly persisted.

Kind regards,

Paul

Feb 8, 2012 at 3:46 PM

All I can now suggest is the following:

1.  Even though you need to deploy this without the UI, presumably there is nothing to stop you testing your model through the UI.  So does your scenario work correctly when doing everything through the Naked Objects UI first of all?

2. I assume that your Application object has a collection of ApplicationRoles and that the Role object also has a collection of ApplicationRoles?  If so, is there any possibility that some method in your code is attempting to add the new ApplicationRole into either of those collections before you have persisted it  -  e.g. because you wrote the code to work with objects in memory as well as with the database? 

Feb 8, 2012 at 3:59 PM


On 8 February 2012 14:53, paul_arnold_ <notifications@codeplex.com> wrote:

From: paul_arnold_

[snip]

I then create and persist an applicationrole instance in it's own transaction, referencing the application and role instances that were previously committed.

This fails with the following error:

"The object is in a detached state. This operation cannot be performed on an ObjectStateEntry when the object is detached."

What am I missing or doing wrong?


I think what you need to do is to re-acquire your references to the Application and Role objects in the third transaction, eg by looking them up from repositories.

The point is that all the objects being modified need to be "known" to EF. I don't know EF in detail, but if it works like other ORMs (and I'm sure it does), then this is your issue.

Give it a try... hopefully it'll be a quick fix for you?

Dan




Read the full discussion online.

To add a post to this discussion, reply to this email (nakedobjects@discussions.codeplex.com)

To start a new discussion for this project, email nakedobjects@discussions.codeplex.com

You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe or change your settings on codePlex.com.

Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at codeplex.com


Feb 8, 2012 at 4:06 PM

Hi Richard,

1.  I am in the process of knocking up the UI to test item 1, but have just been pulled onto another issue, so once I have some conclusion will let you know.

2.  When you say "is there any possibility that some method in your code is attempting to add the new ApplicationRole into either of those collections before you have persisted it ?", do you mean before Container.Persist(ref object) or do you mean before the EndTransaction has been called?

If you mean before Container.Persist(ref object) then no there is no possibility.  Due to my modified T4 poco template (that auto fixes the navigation properties outside of the entity framework) if I try and set the application or role before calling Container.Persist i get a naked objects error stating that the ApplicationRole is not yet proxied so can't be added to the ApplicationRole collection.

If you mean before EndTransaction is called, then yes, the T4 template will result in the new APplicationRole being added.

I am also going to remove my T4 template and retry with your original one.

I'll let you know how it all goes.

Again thanks for the quick responses, it is most appreciated,

Paul

Feb 9, 2012 at 12:19 PM

Hi Richard,

Just thought I would update you with my progress so far:

1.  The UI produces the same error as my non UI code.  I am in the process of further investigation.

2.  I think I have uncovered an unrelated bug during my investigation. 

If you have an explicit object representing a many to many relationship and that object name starts with the same characters as another object, the UI ModelState.IsValid returns false even when all of the required mandatory fields have been supplied.

To test this I had 6 objects, in 2 related sets.

The first set of objects were my original Application, Role and ApplicationRole, where ApplicationRole is the many to many between Application and Role.

The second set of objects were my new investigation objects.  These were called A (representing Application), R (representing Role) and AR (representing ApplicationRole).  These objects contained the same properties, with the exception that they did not include navigation properties to other types that are also present in the original objects.  This was to keep the test as simple as possible initially.

When trying to persist an AR record via the UI, everything worked.  However, if I renamed the AR Type to "ApplicationRoleNew", and then reran the UI, the GenericControllerImpl.ModelState.IsValid method returned false and the UI reported that there were errors that needed correcting.  It appears as though 3 extra entries had been added to the ModelState and were all reporting a mandatory error.  I retried with various substrings of ApplicationRole and the problem remained.  If I changed the name to MapApplicationRole then the model was valid again.

Feb 9, 2012 at 12:49 PM

Just have a bit more information to update you with:

It appears as though the underlying error is:

"Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries."

Feb 9, 2012 at 12:55 PM

"Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries."

Are you saying that you're getting that last error message even when going purely through Naked Objects MVC UI ?   

Feb 9, 2012 at 1:08 PM

If you can generate a very simple standalone project (based on your three classes)  demonstrates the error (presumably by changing a class name as you did) then I would be happy to run it and investigate.

Feb 9, 2012 at 1:17 PM

Thanks I will do.

Our DB is running SQL Server 2008 R2 SP1.  Will you be able to restore from a backup?

Feb 9, 2012 at 1:47 PM

We have SQL Server on our server  -  though I'd prefer just to test on my local SQl Express.  But presumably I can just create a new database from the .edmx ?

Feb 9, 2012 at 3:33 PM

Hi Richard,

I believe that I have gotten to the bottom of the issue.

The ApplicationRole table has a trigger on it.  I saw this prior to opening this discussion and disabled it in the database via SQL Server Management Studio.  Looks like even though SQL Server Management studio was reporting that the trigger was disabled successfully, that it wasn't.

I have just asked our DBA to walk through the schema/triggers/fks etc with me to check I haven't missed anything.  During this walkthrough he noticied the trigger and asked why it was disabled.  He suggested re-enabling and disabling the trigger again under his user profile.  Since then issue has disappeared.  I have retested with the trigger re-enabled and the issue is reproduceable.  The trigger is the cause.

Thanks for all of your help and suggestions.

Paul

 

Feb 9, 2012 at 3:49 PM

Glad you got to the bottom of this.  I assume that this solves your original problem.

Do you think that the 'unrelated bug' you found along the way is still the case  -  or has that gone away also.  If it is still the case, please do take the time to send us a reproducable simple example so we can investigate.

Feb 9, 2012 at 4:50 PM

1.  It does solve the initial problem.

2. Unrelated bug still occurs in my company product's project.

3. Haven't got around to reproducing unrelated bug in standlone project.  When I get a spare clock cycle and am able to reproduce it I will send you a sample :) And if I can't reproduce it, I will let you know also.