csla: Failed execution of ViewModelBase.SaveAsync causes EditLevel Mismatch error after second SaveAsync attempt

Perhaps i’m doing something wrong, but I noticed that if ViewModelBase.SaveAsync fails after ApplyEdit is executed, a second attempt to call SaveAsync causes an EditLevel Mismatch error.

I think this is because SaveAsync does not set the ViewModelBase.Model back to savable, which means BeginEdit does not refire like it would if you were to set it manually.

At the circled code, ApplyEdit is called as expected. Notice in the second arrow, Model is being set to the result of savable.SaveAsync, which in turn, causes the Model’s setter to call BeginEdit. However, at the third arrow (the catch block), Model is not being set again, so BeginEdit never gets called, meaning the user gets a copy of the list back at the wrong edit level.

tl;dr; If the try block calls ApplyEdit followed by BeginEdit (inside Model.set), shouldn’t the catch block unwind that ApplyEdit and put it back to its prior state (at BeginEdit)

image

Version and Platform CSLA version: 4.9 OS: iOS, Android Platform: Xamarin

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Comments: 17 (16 by maintainers)

Most upvoted comments

Closing this thread as it appears 4.11.1 seems to have resolved this problem. Thank you @rockfordlhotka!

I may be able to get to this in the next couple weeks. You could also do a PR for the change (against the 4.x and master branches) if you want it faster.

That whole method is not implemented terribly well is it? It should probably look more like this:

    protected virtual async System.Threading.Tasks.Task<T> SaveAsync()
    {
        UnhookChangedEvents(Model);
        var savable = Model as Csla.Core.ISavable;
        if (ManageObjectLifetime)
        {
            // clone the object if possible
            ICloneable clonable = Model as ICloneable;
            if (clonable != null)
            savable = (Csla.Core.ISavable)clonable.Clone();

            //apply changes
            var undoable = savable as Csla.Core.ISupportUndo;
            if (undoable != null)
            undoable.ApplyEdit();
        }

        Error = null;
        IsBusy = true;
        OnSaving(Model);
        try
        {
            Model = (T) await savable.SaveAsync();
        }
        catch (Exception ex)
        {
            Model = Model;
            HookChangedEvents(Model);
            Error = ex;
        } 
        IsBusy = false;
        OnSaved();
        return Model;
    }