n level undo break after csla.dll upgrade when there is no data binding

n level undo break after csla.dll upgrade when there is no data binding

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


cslauser posted on Thursday, July 30, 2009

Hi,

Recently when I upgrade the csla.dll from 2.0.1.0 to 3.5.3.0, my application breaks.  When I look through the upgrade log, I found that there is a breaking change in csla 3.0.2 for people who uses data binding.

I am not using data binding at all.  I even call DisableIEditableObject on my BO just in case I my code  inadvertently bind to data somewhere.  I rely totally on manually calling BeginEdit, ApplyEdit and CancelEdit and I always maintained the symmetry of BeginEdit matched by ApplyEdit or CancelEdit.   Now when I click the OK button or a Cancel button, I get an error of the object's EditLevel is mismatched with its parent's EditLevel (I forgot the exact wording, but it is something to that effect).  When I revert back the csla.dll to 2.0.1.0, everything works again.

I looked through the forum and find that the problem always has to do with data binding.  I want to know if anybody not using data binding has similiar problem as mine.

Kam

RockfordLhotka replied on Thursday, July 30, 2009

The change doesn't apply only to data binding, it applies to any code using the undo features of CSLA .NET.

The change is pretty straightforward - CSLA now ensures that ApplyEdit() or CancelEdit() isn't called on a parent when any child has an elevated edit level. The change is specifically designed to help people find bugs in their code as they call the undo methods on their objects.

The most common source of bugs was incorrect use of Windows Forms data binding, which is why you find all sorts of threads and discussion on that topic.

But it is quite possible to have a mismatch in hand-written code as well.

cslauser replied on Thursday, July 30, 2009

Hi Rocky,

Thanks for your quick reply.

So does this mean that somewhere in my code, the BeginEdit is not properly matched with an ApplyEdit or CancelEdit?

If this is the case, does this mean that in the older version of csla.dll, mismatch of BeginEdit and ApplyEdit or CancelEdit is tolerated?  Since everything works fine when I revert to my older version of csla.dll.

I have been trying to fix this for several days and I am pretty sure my BeginEdit is properly matched.  But again I might be wrong.

On a related note, in one of your reply to another user, you said we can expose the edit level for easier debugging of the BO by

Public ReadOnly Property CurrentEditLevel() As Integer
  Get
    Return EditLevel
  End Get
End Property

I tried this on the BO but didn't find the EditLevel in intellisense.  What did I miss?

Kam

RockfordLhotka replied on Thursday, July 30, 2009

Yes, older versions of CSLA didn’t enforce edit levels, and so tolerated mismatches (with various nasty and subtle side-effects).

 

EditLevel doesn’t appear in intellisense because it is marked as an Advanced member (or maybe to never appear). That doesn’t mean it isn’t there – just that it is hidden from intellisense because there’s no normal scenario where you’d want to see it.

 

Jack replied on Thursday, July 30, 2009

Rocky,

Thats a nice 'debugging' feature I didn't know existed.  Maybe we can start another 'FAQ' on debugging or Tips & Tricks where some of these subtle things can go.

There must be a dozen (or so) issues that everybody, especially new to CSLA or new to your methodologies, goes through.  I know a lot of the CSLA veterans often have tips to share.

Many of them are framework vs. development environment (SL vs. Winforms)

jack 

cslauser replied on Monday, August 03, 2009

Hi Rocky,

I exposed the BO's EditLevel and found that the child collection's EditLevel is not correct, as expected.  I  think my problem is that the child collection is presented to the UI dialog box at the wrong level.

If I am not mistaken, the pattern of the UI for csla is that if you want to add, edit a child BO, you open up a new dialog box.  In the dialog box load event, you call BeginEdit to take a snapshot of the BO.  Then when you dismiss the dialog box, you either match it with a ApplyEdit or CancelEdit, depending whether you click the OK button or the Cancel button.  Then if you want to add, edit a child BO of this child BO, you open up another level of dialog box, call BeginEdit in this dialog box load event and so on.  That is, successive child BO has to be add, edit on its own level of dialog box.

What happen in my UI code I think is that I expose the child BO on the wrong level, its parent BO's level dialog box.  Hence the mismatch in EditLevel.  Also I use lazy loading pattern for the child BO collection property.  That is, the child BO collection was not created when the object tree was fetched, rather it only get created the first time the child BO collection was accessed.  I wonder whether this has anything contributing to the mismatch in EditLevel.

Now since I know what the child BO's EditLevel should be and what it actually is, I tried to manually call the child BO's BeginEdit the right amount of times in an attempt to make it right.  But I got an error saying I cannot directly call a child BO's BeginEdit.

So my question is, how to fix this kind of EditLevel mismatch problem?

Kam

RockfordLhotka replied on Monday, August 03, 2009

CSLA doesn’t really care what your UI does, as long as it follows one basic rule.

 

Every BeginEdit() on an object must be matched by a CancelEdit() or ApplyEdit() on that object before the parent object’s edit level is altered.

 

The edit level mismatch exception occurs if you don’t follow that rule.

 

The only exception here, is if you use IEditableObject (or data binding, which uses IEditableObject), in which case there are other rules. You aren’t using that, so it should have no impact.

 

There are no rules around calling these methods on parent or child objects. You can call these methods on any editable object (BusinessBase or BusinessListBase subclass).

 

Rocky

 

cslauser replied on Monday, August 03, 2009

Hi Rocky,

You said "There are no rules around calling these methods on parent or child objects. You can call these methods on any editable object (BusinessBase or BusinessListBase subclass)".  But when I call the BeginEdit of a BO, I got an error from csla saying I cannot directly call the child's BO BeginEdit.  What causes this error?

Kam

RockfordLhotka replied on Monday, August 03, 2009

What is the exception?

cslauser replied on Tuesday, August 04, 2009

The exception is thrown from the csla BusinessListBase BeginEdit method.  The exception message is "BeginEdit is not valid on a child object".

This exception is triggered when I called a child collection's BeginEdit in the load event of a dialog box in an attempt to make the EditLevel right (Since I knew what its parent BO EditLevel and the child collection EditLevel were).  I tried calling this both before and after the parent of this child collection BeginEdit and both get this exception.

You mentioned that EditLevel can get mess up if BeginEdit is not matched by ApplyEdit or CancelEdit but I am pretty sure in my code they are all matched up.

Kam

RockfordLhotka replied on Tuesday, August 04, 2009

Ahh, yes, a collection is different.

 

You can’t explicitly call the methods on a child collection because the collection is part of the parent, and so its edit level is bound directly to the parent’s edit level.

 

You need to call the methods on each item in the collection if you want that particular item to have its own edit state.

cslauser replied on Tuesday, August 04, 2009

OK.  So here is the problem.  Since the collection is part of the parent and its edit level is bound directly to its parent, what is/are the circumstance(s) that will make a child collection different than its parent BO?

Here is what I observed:

By exposing the EditLevel, I found the Parent BO is at edit level 2.  I found its child collection is at edit level 0.  There is no object in the child collection, in other words the child collection is empty.  This is the reason that prompt me to call the child collection's BeginEdit in the first place.  To artificially inflate the child collection's edit level in order to make my program not throw an exception by csla.

Since I am not using databinding at all, the only possibility that causes this problem I know of at this point is a BeginEdit - ApplyEdit mismatch or a BeginEdit - CancelEdit mismatch.  Am I right?

Kam

RockfordLhotka replied on Tuesday, August 04, 2009

See if you can replicate your issue using this code:

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using Csla;

 

namespace UndoTest

{

  class Program

  {

    static void Main(string[] args)

    {

      var root = new Root();

      Console.WriteLine("Root      {0}", root.EditLevel);

      Console.WriteLine("ChildList {0}", root.EditLevel);

      root.BeginEdit();

      Console.WriteLine("Root      {0}", root.EditLevel);

      Console.WriteLine("ChildList {0}", root.EditLevel);

      root.CancelEdit();

      Console.WriteLine("Root      {0}", root.EditLevel);

      Console.WriteLine("ChildList {0}", root.EditLevel);

 

      Console.ReadLine();

    }

  }

 

  [Serializable]

  public class Root : BusinessBase<Root>

  {

    public int EditLevel { get { return base.EditLevel; } }

 

    private static PropertyInfo<ChildList> ChildListProperty = RegisterProperty<ChildList>(c => c.ChildList);

    public ChildList ChildList

    {

      get { return GetProperty(ChildListProperty); }

    }

 

    public Root()

    {

      LoadProperty(ChildListProperty, new ChildList());

    }

  }

 

  [Serializable]

  public class ChildList : BusinessListBase<ChildList, Child>

  {

    public int EditLevel { get { return base.EditLevel; } }

  }

 

  [Serializable]

  public class Child : BusinessBase<Child>

  {

    public int EditLevel { get { return base.EditLevel; } }

  }

}

Copyright (c) Marimer LLC