Hi
I got this error, can someone please explain/ assist.
scenario: A business object which has no properties of its own, but a different child objects.
ie. an Employee with a child of Personal Details.
DataPortal.Update failed (Csla.Core.UndoException: Edit level mismatch in CopyState
at Csla.Core.UndoableBase.CopyState(Int32 parentEditLevel)
at Csla.Core.UndoableBase.CopyState(Int32 parentEditLevel)
at Csla.Core.BusinessBase.BeginEdit()
at Csla.Core.BusinessBase.IEditableObject_BeginEdit()
at System.Windows.Forms.CurrencyManager.OnCurrentChanged(EventArgs e)
at System.Windows.Forms.CurrencyManager.ChangeRecordState(Int32 newPosition, Boolean validating, Boolean endCurrentEdit, Boolean firePositionChange, Boolean pullData)
at System.Windows.Forms.CurrencyManager.List_ListChanged(Object sender, ListChangedEventArgs e)
at System.Windows.Forms.BindingSource.OnListChanged(ListChangedEventArgs e)
at System.Windows.Forms.BindingSource.InnerList_ListChanged(Object sender, ListChangedEventArgs e)
at System.ComponentModel.BindingList`1.OnListChanged(ListChangedEventArgs e)
at System.ComponentModel.BindingList`1.Child_PropertyChanged(Object sender, PropertyChangedEventArgs e)
at Csla.Core.BindableBase.raise_PropertyChanged(Object sender, PropertyChangedEventArgs e)
at Csla.Core.BindableBase.OnUnknownPropertyChanged()
at Csla.Core.BusinessBase.MarkClean()
at Csla.Core.BusinessBase.MarkOld()
at HumanResources.Library.Employee.DataPortal_Update() in C:\Source\KISS\HumanResources.Library\Employee\Employee.vb:line 151)
Thanks
Ellie
When I change to CSLA 3.0 and I have a BusinessListBase as a root and bind it to a datagrid and do an edit in the grid on a object in the blb and I get the EditLevel Mismatch once I come of the field or row. Everything else works.
It seems to be a grandchild not getting an editlevel increase when the parent does.
Thanks in advance.
John
Non-Hierarchical
Object Graph causes EditLevel problems
I’m
surprised more people don’t experience this problem. One scenario where it happens is when the
problem domain is not a strict hierarchy.
We have an application where we would like to have properties of objects
that reference other properties of the same object.
In our
case, we’re working in criminal justice and we have a case with various offence
codes – it’s a collection of child objects (ECOffenceCodes) within the parent
(ERCase). However, in addition, we also
want to work with the Primary Offence Code which is one of the objects in the
collection.
The trouble
is that CSLA expects all Parent-Child relationships to be a hierarchy so upping
the EditLevel of a parent recursively ups the EditLevel of all of its children
(and their children and so on). However,
what if two of its child objects at whatever point in the hierarchy point to
the SAME object instance within the object graph? Blam!
EditLevel mismatch in CopyState.
I have
produced a minimal set of code to illustrate the problem. Create yourself a Windows application project
with a form and a button with the following click event…
using System.Windows.Forms;
namespace EditLevelProblem
{
public class frmMain : Form
{
public frmMain()
{
InitializeComponent();
}
private void btnProblem_Click(object sender, EventArgs e)
{
// Create a parent object
//
ERParent erParent = ERParent.New();
// Add 2 children to its collection
//
for (int i = 0; i < 2; i++)
{
erParent.ChildCollection.Add(ECChild.New());
}
// Here's the nub of the problem
// Point to a child via ANOTHER property of the parent
//
erParent.PrimaryChild = erParent.ChildCollection[1];
// Light blue touch paper and retire...
//
erParent.BeginEdit();
}
}
}
BusinessObjects.cs – a minimal set of CSLA Objects. I’ve put all 3 business objects – ERParent,
ECChild and EcChildCollection into one source module to keep things
simple. No data access, no validation,
authorisation etc. This is about as bare as I could get it (actually the getter
for ERParent.PrimaryChild isn’t used but it keeps ReSharper quiet!) but it
illustrates the problem well…
using System;
using Csla;
namespace EditLevelProblem
{
// ERParent - Parent Class
//
[Serializable()]
public class ERParent : BusinessBase<ERParent>
{
private ECChild _primaryChild = ECChild.New();
private ECChildCollection _childCollection = ECChildCollection.New();
protected override object GetIdValue()
{
return 0;
}
public ECChild PrimaryChild
{
get { return _primaryChild; }
set { _primaryChild = value; }
}
public ECChildCollection ChildCollection
{
get { return _childCollection; }
}
public static ERParent New()
{
return new ERParent();
}
private ERParent()
{
}
}
// ECChild - Child Class
//
[Serializable()]
public class ECChild : BusinessBase<ECChild>
{
protected override object GetIdValue()
{
return 0;
}
public static ECChild New()
{
return new ECChild();
}
private ECChild()
{
MarkAsChild();
}
}
// ECChildCollection - Collection of Child Objects
//
[Serializable()]
public class ECChildCollection : BusinessListBase<ECChildCollection, ECChild>
{
public static ECChildCollection New()
{
return new ECChildCollection();
}
private ECChildCollection()
{
MarkAsChild();
}
}
}
Maybe what
is required is for CopyState to maintain a hashtable (or some such) of objects
visited during the recursive CopyState and only perform CopyState ONCE per
object instance.?
Is it too radical
to suggest that everywhere an if statement of the form:
if (this.EditLevel + 1 > parentEditLevel)
throw new UndoException(string.Format(Resources.EditLevelMismatchException,
"CopyState"));
UndoableBase.CopyState, UndoChanges, AcceptChanges
BusinessListBase.CopyState, UndoChanges, AcceptChanges
it should
simply read:
if
(this.EditLevel + 1 > parentEditLevel)
return;
In other
words, no child’s EditLevel is ever allowed to get out of step with its
parent’s?
I think
there is a sort of precedent for this sort of behaviour in the comment in
UndoableBase.UndoChanges that reads:
// if we are a child object we might be
asked to
// undo below the level of stacked
states,
// so just do nothing in that case
if (EditLevel > 0)
{
O
This seems to be my problem I have a blb -bo-child and on the UI its a parent datagrid with a datagrids with the child below it. When I edit the childern datagrids I have no problems with a save but when I edit the parent grid I get an error. The objects are Users-user-userrole
Edititing the user is where the edit level mismatch occurs.
Sean Walsh:Non-Hierarchical Object Graph causes EditLevel problems
I’m surprised more people don’t experience this problem. One scenario where it happens is when the problem domain is not a strict hierarchy. We have an application where we would like to have properties of objects that reference other properties of the same object.
In our case, we’re working in criminal justice and we have a case with various offence codes – it’s a collection of child objects (ECOffenceCodes) within the parent (ERCase). However, in addition, we also want to work with the Primary Offence Code which is one of the objects in the collection.
The trouble is that CSLA expects all Parent-Child relationships to be a hierarchy so upping the EditLevel of a parent recursively ups the EditLevel of all of its children (and their children and so on). However, what if two of its child objects at whatever point in the hierarchy point to the SAME object instance within the object graph? Blam! EditLevel mismatch in CopyState.
If I understand you correctly, what you are describing is a using relationship, not a parent-child relationship. You must code using relationships differently, there's no real way around that.
This is one of the more confusing elements of OO design though: confusion between using and forms of containment.
An Order has LineItems, and each LineItem has a Product.
That seems like an innocent statement that would lead to the belief that Product is somehow a child of LineItem. Very clearly however, Product is its own concept. A LineItem merely uses a Product - and in reality probably doesn't use a Product object at all - just some product data.
For convenience of navigation (if your use case requires it), LineItem might implement a GetProduct() method that returns a Product object. Honestly though, even that is a form of coupling that is often best avoided (though I do such things sometimes, because it makes the object model more intuitive).
The key here, is that LineItem never maintains a reference to a Product. Even if it implements a GetProduct() method, that method looks like this:
Public Function GetProduct()
Return Product.GetProduct(_productId)
End Function
It is merely a shortcut for something the UI could have done itself - again for purposes of making the object model and object relationships more intuitive and perhaps more explicit.
But Product is not a child of LineItem.
Getting the basic object design wrong, especially on a point like this, will cause all sorts of nasty headaches - including some with data binding and edit levels.
Thats fine. But what if you have a parent object which has no direct properties, instead it has a series of children which contain a variety of information. ie like the example at the beginning.
Employee (No Properties)
Personal Details
Address Details
Tax Details
etc.
All the children are and should be contained within the parent. How does the Editlevel mismatch issue get resolved.
Thanks
Ellie
Wal972:DataPortal.Update failed (Csla.Core.UndoException: Edit level mismatch in CopyState
Why are you invoking n-level undo in your DataPortal_XYZ methods?
Not that this is a totally invalid scenario, but it is not a common scenario either, and so it is suspicious. It may indicate that you are doing something odd, and it can sometimes be challenging to get odd things working just right.
I'm not intentially invoking n-undo. I am using the same code as in the previous 2.x framework. So any assistance would be appreciated. I not sure of how this is happening.
Thanks
Ellie
My guess is that you are using a local data portal, and that you
are not cloning the object before saving it. This means that the object is still
connected to the UI through data binding during the save and there’s
probably some event-loopback issues going on.
Try saving the object as shown in the current
ProjectTracker\PTWin code, where the object is cloned before saving, and see if
that helps.
Rocky
From: Wal972
[mailto:cslanet@lhotka.net]
Sent: Friday, July 20, 2007 9:46 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] EditLevel Mismatch ?
I'm not intentially invoking n-undo. I am using the same code as in the
previous 2.x framework. So any assistance would be appreciated. I not sure of
how this is happening.
Thanks
Ellie
Thanks that did the trick. Why does local data portal need the cloning ?
Ellie
It doesn’t technically need the cloning, but the
cloning is good for a couple reasons – check Chapter 9 for some
discussion.
In short, saving the clone does two big things: eliminates data
binding (since the original is bound, not the clone), and provides a way to
return to the original object graph in case of an exception during the save.
When using a remote data portal the object is cloned across the
network, so this is a non-issue. But in the local data portal the object itself
is what’s being saved, and that can be a problem.
One item on my wish list is to automatically do a clone when
using the local data portal. This would clearly be a breaking change, but I’ll
probably do it in version 3.5 because it would make this whole issue fade
nicely into history.
Rocky
From: Wal972
[mailto:cslanet@lhotka.net]
Sent: Friday, July 20, 2007 11:26 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] RE: EditLevel Mismatch ?
Thanks that did the trick. Why does local data portal need the cloning ?
Ellie
Rocky:
I notice that you are not cloning the business object when it is being saved as an element of an EditablRootListBase. Is there a reason for this? Such objects will often be bound to the UI, with the Save being initiated by an 'EndEdit' call on a BindingSource. The following is from EditablRootListBase:
public virtual void SaveItem(int index)
{
bool raisingEvents = this.RaiseListChangedEvents;
this.RaiseListChangedEvents = false;
_activelySaving = true;
T item = this[index];
int editLevel = item.EditLevel;
// commit all changes
for (int tmp = 1; tmp <= editLevel; tmp++)item.AcceptChanges(editLevel - tmp);
try{
// do the save this[index] = (T)item.Save();}
finally{
// restore edit level to previous level for (int tmp = 1; tmp <= editLevel; tmp++)item.CopyState(tmp);
_activelySaving =
false; this.RaiseListChangedEvents = raisingEvents;}
this.OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, index));}
Stan
Not cloning the object in ERLB was an "oversight", which has been fixed in current versions of the framework (3.0.2 and 3.5).
I put oversight in quotes, because I knew about the issue, but hadn't come up with an elegant solution. The problem is a bit complex, because you only want to do the cloning if the data portal is running in local mode, not when it is using a remote server.
So in 3.0.2 I added an AutoCloneOnUpdate config setting, so the data portal can actually just take care of this whole issue by itself. In 3.0.2 this defaults to false, and in 3.5 it will default to true.
Now the ERLB does the clone itself (always) if that config setting is false - which makes the local data portal happy, but incurs unneeded overhead with a remote data portal. But if the flag is true, then it allows the data portal to just do the right (most efficient) thing.
See the Using CSLA .NET 3.0 ebook for more details.
Copyright (c) Marimer LLC