CslaDataProvider Async Save

CslaDataProvider Async Save

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


ajj3085 posted on Tuesday, November 11, 2008

Hi,

I wanted to throw this code out there.. it's a change to CslaDataProvider to enable the Save method to respect the IsAsync flag.  I haven't had any issues so far in my limited testing.. I'm hoping others will try it and see if it will help them.

Thanks
Andy

Normal 0

public void Save() {

      // only do something if the object implements

      // ISavable

      Csla.Core.ISavable savable = this.Data as Csla.Core.ISavable;

      if ( savable != null )

            IsBusy = true;

 

      if ( IsAsynchronous ) {

            System.Threading.ThreadPool.QueueUserWorkItem( DoSave, savable );

      }

      else {

            DoSave( savable );

            IsBusy = false;

      }

}

 

private void DoSave( object state ) {

      Csla.Core.ISavable savable = state as Csla.Core.ISavable;

      object result = savable;

      Exception exceptionResult = null;

      try {

            // apply edits in memory

            Csla.Core.ISupportUndo undo = savable as Csla.Core.ISupportUndo;

            if ( undo != null && _manageLifetime )

                  undo.ApplyEdit();

 

            if ( !Csla.ApplicationContext.AutoCloneOnUpdate ) {

                  // clone the object if possible

                  ICloneable clonable = savable as ICloneable;

                  if ( clonable != null )

                        savable = (Csla.Core.ISavable)clonable.Clone();

            }

 

            // save the clone

            result = savable.Save();

 

            if ( !ReferenceEquals( savable, this.Data ) && !Csla.ApplicationContext.AutoCloneOnUpdate ) {

                  // raise Saved event from original object

                  Core.ISavable original = this.Data as Core.ISavable;

                  if ( original != null ) {

                        if ( System.Windows.Application.Current.Dispatcher.CheckAccess() ) {

                              original.SaveComplete( result );

                        }

                        else {

                        System.Windows.Application.Current.Dispatcher.Invoke(

                                    new Action<object>(

                                          ( obj ) => { original.SaveComplete( obj ); }

                                    ),

                                    System.Windows.Threading.DispatcherPriority.Normal,

                                    result

                              );

                        }

                  }

            }

 

            // start editing the resulting object

            undo = result as Csla.Core.ISupportUndo;

            if ( undo != null && _manageLifetime )

                  undo.BeginEdit();

      }

      catch ( Exception ex ) {

            exceptionResult = ex;

      }

 

      if ( !System.Windows.Application.Current.Dispatcher.CheckAccess() )

            System.Windows.Application.Current.Dispatcher.Invoke(

              new Action( () => { IsBusy = false; } ),

              new object[] { }

            );

 

      // clear previous object

      base.OnQueryFinished( null, exceptionResult, null, null );

      // return result to base class

      base.OnQueryFinished( result, null, null, null );

}

 


ajj3085 replied on Wednesday, November 12, 2008

Hi,

Found an issue if there's PropertyStatus controls being used.  I've updated the following event handlers in PropertyStatus.cs to resolve a VerifyAccess exception:

    void source_BusyChanged(object sender, BusyChangedEventArgs e)
    {
        if ( Application.Current.CheckAccess() ) {
            if ( e.PropertyName == Property )
                IsBusy = e.Busy;

            UpdateState();
        }
        else {
            Application.Current.Dispatcher.Invoke(
                new BusyChangedEventHandler( source_BusyChanged ),
                sender,
                e
            );
        }
    }

    private void source_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if ( Application.Current.CheckAccess() ) {
            if ( e.PropertyName == Property || string.IsNullOrEmpty( e.PropertyName ) )
                UpdateState();
        }
        else {
            Application.Current.Dispatcher.Invoke(
                new PropertyChangedEventHandler( source_PropertyChanged ),
                sender,
                e
            );
        }
    }

RockfordLhotka replied on Thursday, November 13, 2008

We considered doing this, and decided against it. The reason we decided against it is because of the PropertyStatus issue you discovered - but in the broader sense.

It is quite possible for other controls, or UI code, or we-don't-know-what, to set up handlers for PropertyChanged events. They would all break if the Save() operation occurred on a background thread. This could cause massive confusion and unhappiness - and possibly unsolvable issues (think about third-party controls that handle the event for example).

ajj3085 replied on Thursday, November 13, 2008

Hmm... I see the concern.  Is this mitigated at all by the BO being cloned, and the clone actually being saved?  Or are there other areas that might cause these events to raise too?

RockfordLhotka replied on Thursday, November 13, 2008

You'd think the cloning would resolve the issue. Sergey did the work on this, so I can't say exactly what issues were there...

Copyright (c) Marimer LLC