This is a migrated thread and some comments may be shown as answers.

IEditableObject Interface

3 Answers 146 Views
General Discussions
This is a migrated thread and some comments may be shown as answers.
This question is locked. New answers and comments are not allowed.
Pål
Top achievements
Rank 1
Pål asked on 11 Mar 2010, 10:12 PM
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

3 Answers, 1 is accepted

Sort by
0
Jan Blessenohl
Telerik team
answered on 12 Mar 2010, 09:17 PM
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.
0
Pål
Top achievements
Rank 1
answered on 13 Mar 2010, 09:14 PM
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
0
Jan Blessenohl
Telerik team
answered on 17 Mar 2010, 04:29 PM
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.
Tags
General Discussions
Asked by
Pål
Top achievements
Rank 1
Answers by
Jan Blessenohl
Telerik team
Pål
Top achievements
Rank 1
Share this question
or