New Posts New Posts RSS Feed: Unable to save a changed entity that has a many to many relation
  FAQ FAQ  Forum Search   Calendar   Register Register  Login Login

Unable to save a changed entity that has a many to many relation

 Post Reply Post Reply
Author
NicoS View Drop Down
Newbie
Newbie


Joined: 21-Jun-2010
Posts: 8
Post Options Post Options   Quote NicoS Quote  Post ReplyReply Direct Link To This Post Topic: Unable to save a changed entity that has a many to many relation
    Posted: 23-Jun-2010 at 10:08am
Hi,
 
Im unable to save a changed entity that has a many to many relation.
 
Steps to reproduce:
  • NorthwindIB model, many to many relationship without payload between Employee.Territories and Territory.Employees.
  • Display a master detail setup where Employee is the master showing Teritories as details and enable saving of a changed Employee.
  • Select an employee with teritories and change the Employee its firstname and save the changes.
  • The change of Employee is not saved now which can be checked on the database and the EntitySaveOperation callback of the SaveChangesAsync method of the manager.
What i have noticed within the exception i got back from the server (EntitySaveOperation) is that on the server DevForce/EF trys to add the Teritories of the Employee to the database again.This shouldnt be happening since only Employee has been changed.
 
Below the master/detail XAML:

<toolkit:DataForm x:Name="EmployeeForm" ItemsSource="{Binding Employees}" CurrentItem="{Binding CurrentEmployee, Mode=TwoWay}" />
<sdk:DataGrid ItemsSource="{Binding ElementName=EmployeeForm, Path=CurrentItem.Territories}" />

 
Below the serverexception found within the EntitySaveOperation:
 
 
System.Data.UpdateException: An error occurred while updating the entries. See the inner exception for details. ---> System.Data.SqlClient.SqlException: Violation of PRIMARY KEY constraint 'PK_EmployeeTerritoryNoPayload'. Cannot insert duplicate key in object 'dbo.EmployeeTerritoryNoPayload'.
The statement has been terminated.
   at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning()
   at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
   at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
   at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result)
   at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(DbAsyncResult result, String methodName, Boolean sendToPipe)
   at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
   at System.Data.Mapping.Update.Internal.DynamicUpdateCommand.Execute(UpdateTranslator translator, EntityConnection connection, Dictionary`2 identifierValues, List`1 generatedValues)
   at System.Data.Mapping.Update.Internal.UpdateTranslator.Update(IEntityStateManager stateManager, IEntityAdapter adapter)
   --- End of inner exception stack trace ---
   at System.Data.Mapping.Update.Internal.UpdateTranslator.Update(IEntityStateManager stateManager, IEntityAdapter adapter)
   at System.Data.Objects.ObjectContext.SaveChanges(SaveOptions options)
   at IdeaBlade.EntityModel.Edm.EdmSaveHelper.ProcessSaves(IEnumerable`1 groupsByType)
   at IdeaBlade.EntityModel.Edm.EdmSaveHelper.Save()
 
TIA, Best regards,
Nico Schoemaker.
Back to Top
GregD View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 09-May-2007
Posts: 374
Post Options Post Options   Quote GregD Quote  Post ReplyReply Direct Link To This Post Posted: 23-Jun-2010 at 11:53am
NicoS:

I think this error is somehow an artifact of your UI. I can run the following code successfully in my demo harness. I am assuming you've left the getters for both Employee.Territories and Territory.Employees as public, so we don't have effects of the bug related to that.


    public void TestManyToManySave() {
      _localOutput.Append(String.Format("[{0}] Started...\n", _nameTestManyToManySave));
      Utility.ResetEntityManager(_em1);

      var query = _em1.Employees.Where(e => e.EmployeeID == 1);
      query.ExecuteAsync<Employee>(GotEmployeeToSave, null);
    }

    public void GotEmployeeToSave(EntityQueryOperation<Employee> op) {
      if (op.HasError) {
        _localOutput.Append(String.Format("[{0}] Error: {1}\n",
          _nameTestManyToManySave, op.Error.Message));
      }
      else {
        Employee anEmployee = op.Results.First();
        _localOutput.Append(String.Format("[{0}] Employee: {1}\n",
          _nameTestManyToManySave, anEmployee.LastName));
        anEmployee.LastName += "X";

        _em1.SaveChangesAsync(SaveCompleted);
      }
    }


    public void SaveCompleted(EntitySaveOperation op) {
      if (op.HasError) {
        _localOutput.Append(String.Format("[{0}] Error: {1}\n",
          _nameTestManyToManySave, op.Error.Message));
      }
      else {
        _localOutput.Append(String.Format("[{0}] Save completed successfully\n",
          _nameTestManyToManySave));
      }
      Utility.FlushOutputBuffer(_localOutput, _resultsReporter);
    }

Back to Top
NicoS View Drop Down
Newbie
Newbie


Joined: 21-Jun-2010
Posts: 8
Post Options Post Options   Quote NicoS Quote  Post ReplyReply Direct Link To This Post Posted: 23-Jun-2010 at 12:38pm
Hi Greg,
 
Thanks for your response.
> so we don't have effects of the bug related to that
Correct, got a test harness that is not related to the other many to many issue ;-)
 
The diference between your and my test harness is that the binding of the datagrid automaticly gets the related Teritory objects through Path=CurrentItem.Territories when i select an Employee in the GUI.
<sdk:DataGrid ItemsSource="{Binding ElementName=EmployeeForm, Path=CurrentItem.Territories}" />
So i have no code involved getting the related Teritories of the current employee.
I can monitor that the manager is automaticly fetching the related Teritories through the Fetching event and they show up in the grid after that.
Saving a changed Employee with automaticly fetched teritories now will fail.
 
I did an additional test, i used this linq query to populate the DataForm:
var query = _mgr.Employies;
Now when i change this to:
var query = _mgr.Employies.Include("Territories");
I have no problems saving a changed Employee now !!
So my best guess is that somehow the automatic binding screws up the manager its cache and or entity state(s).
 
Hth, Beste regards
Nico Schoemaker.
Back to Top
NicoS View Drop Down
Newbie
Newbie


Joined: 21-Jun-2010
Posts: 8
Post Options Post Options   Quote NicoS Quote  Post ReplyReply Direct Link To This Post Posted: 24-Jun-2010 at 10:42am
Hi Greg,
 
Were you able to reproduce it now ?
 
Best regards,
Nico Schoemaker
Back to Top
GregD View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 09-May-2007
Posts: 374
Post Options Post Options   Quote GregD Quote  Post ReplyReply Direct Link To This Post Posted: 24-Jun-2010 at 1:08pm
I think so - I added statements to retrieve the changed Employee's Territories before attempting to save the Employee, and the save did blow up, though not with very helpful error information. I'm going to convert my example to a test case and send it on to the chief developer.

By the way, if you want a copy of the test harness I'm using, submit a Support Request http://www.ideablade.com/TechSupport/CustomerSupportRequestForm.aspx and I'll send it to you. Things that involve somebody else's UI controls are extremely difficult to debug - too much "black box" stuff going on.

Edited by GregD - 25-Jun-2010 at 10:30am
Back to Top
NicoS View Drop Down
Newbie
Newbie


Joined: 21-Jun-2010
Posts: 8
Post Options Post Options   Quote NicoS Quote  Post ReplyReply Direct Link To This Post Posted: 25-Jun-2010 at 1:01am
Hi Greg,
 
Mail send.
 
Thanks !!!
 
Back to Top
NicoS View Drop Down
Newbie
Newbie


Joined: 21-Jun-2010
Posts: 8
Post Options Post Options   Quote NicoS Quote  Post ReplyReply Direct Link To This Post Posted: 25-Jun-2010 at 1:42pm
Hi Greg,
 
Your test harness reproduces the problem im having with my test.
When the PostNavPropRetrievalSaveCompleted gets called the op.Exception.RemoteExceptionDetails contains the same database exception as in my test. Imho the server trys to add the already exsisting territories of the employee to the dbo.EmployeeTerritoryNoPayload table.
 
Thanks,
Nico Schoemaker.
 
Below the RemoteExceptionDetails :
 
System.Data.UpdateException: An error occurred while updating the entries. See the inner exception for details. ---> System.Data.SqlClient.SqlException: Violation of PRIMARY KEY constraint 'PK_EmployeeTerritoryNoPayload'. Cannot insert duplicate key in object 'dbo.EmployeeTerritoryNoPayload'.
The statement has been terminated.
  at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)
   at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning()
   at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
   at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
   at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result)
   at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(DbAsyncResult result, String methodName, Boolean sendToPipe)
   at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
   at System.Data.Mapping.Update.Internal.DynamicUpdateCommand.Execute(UpdateTranslator translator, EntityConnection connection, Dictionary`2 identifierValues, List`1 generatedValues)
   at System.Data.Mapping.Update.Internal.UpdateTranslator.Update(IEntityStateManager stateManager, IEntityAdapter adapter)
   --- End of inner exception stack trace ---
   at System.Data.Mapping.Update.Internal.UpdateTranslator.Update(IEntityStateManager stateManager, IEntityAdapter adapter)
   at System.Data.EntityClient.EntityAdapter.Update(IEntityStateManager entityCache)
   at System.Data.Objects.ObjectContext.SaveChanges(SaveOptions options)
   at IdeaBlade.EntityModel.Edm.EdmSaveHelper.ProcessSaves(IEnumerable`1 groupsByType)
   at IdeaBlade.EntityModel.Edm.EdmSaveHelper.SaveWithinContext()
   at IdeaBlade.EntityModel.Edm.EdmSaveHelper.Save() 
 
 
 
Back to Top
 Post Reply Post Reply

Forum Jump Forum Permissions View Drop Down