IEditableObject Interface

Thread is closed for posting
4 posts, 0 answers
  1. Pål
    Pål avatar
    53 posts
    Member since:
    Sep 2012

    Posted 11 Mar 2010 Link to this post

    Hi.

    I'm looking to implement the IEditableObject interface in a base persistent class, so that all descendants would automatically benefit from this implementation. Could you please give some pointers on how to achieve this, if possible?

    Most important is of course offline usage, where ObjectContainer is involved rather than ObjectScope.

    Typically usage is when an object is bound to a form with 'OK' 'Cancel' 'Apply' buttons. But also in grids etc.

    Maybe IObjectContext methods could be used, since they are shared between IObjectContainer and IObjectScope.

    Any feedback or thoughts would be much appreciated.

    Thanks

    Pål
  2. Jan Blessenohl
    Admin
    Jan Blessenohl avatar
    707 posts

    Posted 12 Mar 2010 Link to this post

    Hi Pål,
    It is not possible at the moment to really roll back the changes. This is exactly the missing part to support nested transactions. 

    What you can do in your base class implementation is a refresh in case of scope and a Server call with the id to get the data again from the db and Apply() it to the container.

    Sincerely yours,
    Jan Blessenohl
    the Telerik team

    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items.
  3. Pål
    Pål avatar
    53 posts
    Member since:
    Sep 2012

    Posted 13 Mar 2010 Link to this post

    Hi Jan.

    I was afraid that was the case...

    I'm also afraid your solution will not completely satisfy the IEditableObject interface: When a call to BeginEdit() is made on an already dirty instance, CancelEdit would then return the instance to the persistent state rather than the pre-BeginEdit() state.

    I have found a solution which will work, but will also make unnecesary calls to the backend storage / business layer since a property will always be marked as dirty event if the original value is re-set. So my next question then is: Can you mark a property non-dirty. E.g.. IObjectContext.MakeNonDirty(String propertyName)? I'm completely happy using reflection for this...

    Here is my IEditableObject implementation. A very crude implementation using reflection to create a snapshot of all read/write properties at BeginEdit() call:
        public abstract class EditableObject : IEditableObject {  
     
            /// <summary>  
            /// The snapshot created when IEditableObject.BeginEdit is called.  
            /// </summary>  
            private Dictionary<PropertyInfo, object> m_IEditableObjectSnapshot;  
     
            /// <summary>  
            /// The cache of read/write properties to minimize reflection for IEditableObject.  
            /// </summary>  
            private List<PropertyInfo> m_ReadWriteProperties;  
     
            /// <summary>  
            /// Begins an edit on this instance.  
            /// </summary>  
            private void BeginEdit() {  
     
                // Create new dictionary to hold current values of read/write properties  
                m_IEditableObjectSnapshot = new Dictionary<PropertyInfo, Object>();  
     
                // Loop through all read/write properties in this  
                foreach (PropertyInfo currentProperty in this.GetReadWriteProperties()) {  
     
                    // Get current value of current property  
                    Object currentValue = this.GetPropertyValue(currentProperty);  
     
                    // Add current value to dictionary using property as key  
                    m_IEditableObjectSnapshot.Add(currentProperty, currentValue);  
                }  
            }  
     
            /// <summary>  
            /// Discards changes since the last <see cref="IEditableObject.BeginEdit"/>   
            /// was called.  
            /// </summary>  
            private void CancelEdit() {  
     
                // Check if snapshot of property values exists  
                if (m_IEditableObjectSnapshot != null) {  
     
                    // Create binding flags for property set invocation  
                    BindingFlags flags = BindingFlags.Instance | BindingFlags.SetProperty | BindingFlags.Public;  
     
                    // Loop through all PropertyInfo/Value pairs in dictionary  
                    foreach (var currentEntry in m_IEditableObjectSnapshot) {  
     
                        // Get name of current property  
                        String name = currentEntry.Key.Name;  
     
                        // Wrap original value in array  
                        Object[] value = new object[] { currentEntry.Value };  
     
                        // Invoke property setter restoring original value  
                        this.GetType().InvokeMember(name, flags, nullthis, value);  
                    }  
                }  
     
                // Reset property values snapshot  
                m_IEditableObjectSnapshot = null;  
            }  
     
            /// <summary>  
            /// Pushes changes since the last call to <see cref="IEditableObject.BeginEdit()"/>   
            /// or <see cref="IBindingList.AddNew()"/> into this instance.  
            /// </summary>  
            private void EndEdit() {  
     
                // Reset property values snapshot  
                m_IEditableObjectSnapshot = null;  
            }  
     
            /// <summary>  
            /// Gets the public properties of this instance which can be read and set externally.  
            /// </summary>  
            /// <returns>A list of public read/write properties.</returns>  
            private List<PropertyInfo> GetReadWriteProperties() {  
     
                // Check if RW properties are missing  
                if (m_ReadWriteProperties == null) {  
     
                    // Create dictionary to hold read/write properties  
                    m_ReadWriteProperties = new List<PropertyInfo>();  
     
                    // Loop through all public properties in this type  
                    foreach (PropertyInfo currentProperty in this.GetType().GetProperties()) {  
     
                        // Check if property is both read and write  
                        if ((currentProperty.CanRead) && (currentProperty.CanWrite)) {  
     
                            // Add current property to lisr  
                            m_ReadWriteProperties.Add(currentProperty);  
                        }  
                    }  
                }  
     
                // Return list of RW properties  
                return m_ReadWriteProperties;  
            }  
     
            /// <summary>  
            /// Gets the property value of the supplied property.  
            /// </summary>  
            /// <param name="property">The property.</param>  
            /// <returns>The value of the property.</returns>  
            private object GetPropertyValue(PropertyInfo property) {  
     
                // Create binding flags for property get invocation  
                BindingFlags flags = BindingFlags.Instance |  
                                     BindingFlags.GetProperty |  
                                     BindingFlags.FlattenHierarchy |  
                                     BindingFlags.NonPublic |   
                                     BindingFlags.Public;  
     
                // Invoke property get on this and return value  
                return this.GetType().InvokeMember(property.Name, flags, nullthisnull);  
            }  
        }  
     

    Thanks

    Pål
  4. Jan Blessenohl
    Admin
    Jan Blessenohl avatar
    707 posts

    Posted 17 Mar 2010 Link to this post

    Hi Pål,
    Marking a property non dirty is only half of the code, you want to reset the value as well which leads possibly to a database turnaround as well.

    I do not see a way to do this via reflection, it is far too complecated.

    Sincerely yours,
    Jan Blessenohl
    the Telerik team

    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items.
Back to Top