Saving help in 3.7.1

Saving help in 3.7.1

Old forum URL: forums.lhotka.net/forums/t/7816.aspx


kamanova posted on Monday, October 19, 2009

Hello, I've recently upgraded to 3.7.1 and modified my save function based on ptracker.
However, I am still receiving the "EditLevelMismatch in CopyState" exception when saving anything with a child collection.

Any suggestions on what I am doing wrong in the following code is greatly appreciated.
I apologize for the formatting.


public static T SaveRoot(T businessObject, BindingSource bindingSource, List childBindingSourceList, bool rebindDataSource) where T : BusinessBase
{
if (bindingSource != null)
{
bindingSource.RaiseListChangedEvents = false;
UnbindBindingSource(bindingSource, true, true);

if (childBindingSourceList != null)
{
foreach (BindingSource childBindingSource in childBindingSourceList)
{
childBindingSource.RaiseListChangedEvents = false;
UnbindBindingSource(childBindingSource, true, false);
childBindingSource.DataSource = bindingSource;
}
}
}

T Clone = businessObject.Clone();
Clone.ApplyEdit();

try
{
businessObject = Clone.Save();
businessObject.BeginEdit();

if (rebindDataSource)
{
if (bindingSource != null)
{
bindingSource.DataSource = null;
if (childBindingSourceList != null)
{
foreach (BindingSource childBindingSource in childBindingSourceList)
{
childBindingSource.DataSource = bindingSource;
}
}
bindingSource.DataSource = businessObject;
}
}
}
catch (ValidationException ex)
{
throw new BusinessBaseValidationException(Clone, ex.Message, ex);
}
catch (DataPortalException ex)
{
MessageBox.Show(ex.BusinessException.ToString(), "Error saving", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "Error Saving", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
finally
{
if (bindingSource != null)
{
bindingSource.RaiseListChangedEvents = true;
bindingSource.ResetBindings(false);

if (childBindingSourceList != null)
{
foreach (BindingSource childBindingSource in childBindingSourceList)
{
childBindingSource.RaiseListChangedEvents = true;
childBindingSource.ResetBindings(false);
}
}
}
}

return businessObject;
}

private static void UnbindBindingSource(BindingSource source, bool apply, bool isRoot)
{
System.ComponentModel.IEditableObject current =
source.Current as System.ComponentModel.IEditableObject;
if (isRoot)
source.DataSource = null;
if (current != null)
if (apply)
current.EndEdit();
else
current.CancelEdit();
}


Thanks!

RockfordLhotka replied on Monday, October 19, 2009

The easiest thing to do is switch to the CslaActionExtender. This control wraps all the ugly bindingsource management so you don't have to do it by hand.

saeedsamie replied on Monday, October 19, 2009

Hi Kamanova,

I read your code briefly and what appeared to me at a glance is that you first unbind the root binding source and then children binding sources which should be reverse. But you may exploit of BindingSourceNode and BindingSourceHelp in Csla.Windows namespace to handle all of these works for you. Rocky has well explained using these extenders in his book.




kamanova replied on Tuesday, October 20, 2009

Thank you for the prompt replies.

Using the CslaActionExtender control I get an EditLevelMismatch exception in BusinessListBase.AcceptChanges.
During the UndoableBase.AcceptChanges method it will loop through the child collections once okay, then the next time EditLevel is 0 and BindingEdit is false and this is when the exception gets thrown.

Which book has the BindingSourceNode explanation?


Thanks again for your help.

-Karl

kamanova replied on Tuesday, October 20, 2009

Sorry, I see it is in the 2008 book. (I have the 2005 print and the Using CSLA 3.0 ebook.)

JonnyBee replied on Tuesday, October 20, 2009

Hi,

There are situations where I do NOT find the CslaActionExtender useful:
1. When you want to do an asyncronous save.
2. When you want to control dialog result on a modal form based on whether Save was OK or got an Error. (ie I don't want the form to close if I got an error).

Another  issue I have is that the CslaActionExtender makes a clone of my object graph before Save and so does the dataportal again when save is called on LocalProxy.

From what I have experienced the DataPortal.Save will always return a new object (that is unless running on LocalProxy and AutoCloneOnUpdate is set to false).  AutoCloneOnUpdate is true by default.

If you want to understand more on why you get the error have a look at my blog post on Forms DataBinding – The magic sequence of BindUI/UnbindUI

tiago replied on Sunday, September 09, 2012

JonnyBee
Hi,

Another  issue I have is that the CslaActionExtender makes a clone of my object graph before Save and so does the dataportal again when save is called on LocalProxy.

You are right: One clone too many. The worst part is that the clone isn't used for any purpose. In fact it should be used to restore the object to a known state when the save operation fails.

Copyright (c) Marimer LLC