Add Reusable (Inheritable) Commit/Rollback (Cancel Edit) Functionality To Your .NET Business Objects by Implementing IEditableObject

The IEditableObject interface in .NET is part of the System.Component model namespace and is used to provide functionality to your objects that implement this interface that allows the object to commit or roll back changes.  Typically your business objects will implement this interface in order for them to natively have the commit and rollback functionality you desire.  When using the business object as a data source that is bound to a control such as a grid control, the grid control recognizes the IEditableObject implementation and interfaces with it directly in order to achieve the desired commit/rollback functionality desired without having to write any other custom code in your UI layer or for the grid component.

We make heavy use of 3rd party components within our applications, and a big one that we use is the Infragistics WinGrid.  This provides excellent capability and works great with .NET datasets.  It also integrates well with business objects or lists of business objects as a data source, but unless your business object implements IEditableObject you will not get the native commit/rollback functionality you would get with a dataset.  For example – the user starts changing a record on a grid in a windows form and hits the Esc key twice to cancel their changes – this will not actually rollback these changes to the business object unless the business object contains logic to do so.

Like a lot of things when it comes to software development there are a few ways to accomplish this 1) A quick and dirty way that might work for the current business object, but requires a lot of customization for each additional business object that you want to introduce the functionality to 2) A thought out where we are really thinking about the architecture and that allows you to write the code once and re-use that same code where needed

Ok, so we have a base class that we created and we called it BusinessObjectBase and it implements IEditableObject.  The three methods of IEditableObject are pretty self describing – BeginEdit, CancelEdit, and EndEdit.

Here’s how we do it:

1) Created a MustInherit Class (Abstract in C#) – This is the class that all business object that we want to incorporate this functionality into will Inherit

2) Implement IEditableObject

3) Write code for each of the three methods that will give us this desired functionality. 

BeginEdit – Copies the current object (itself) into a new object of the same type.  This uses the MemberwiseClose method in order to create a copy of the current object to be used as the object we will revert to if the changes are cancelled.  This allows us to reuse this functionality as is without having to customize it for other object that have different property definitions.  We also set an internal boolean variable used as a flag to signify ‘edit mode’ to true.

CancelEdit – Uses reflection to copy the property values from the copied object that was created in the BeginEdit method.  It cycles through all of the public properties using reflection and copies those values back to the current object overwriting any changes made during the edit.  The MemberwiseClone method in this case could not be used because an object instance cannot set itself to reference another object from within itself.  Additional functionality was added to CancelEdit so that it would remove itself from the parent list that it was in if it was a new object.  This was put in place because grid components automatically add new objects to a list when adding a new object to the grid, so in this case if the edit is cancelled then we want to destroy the object as it never existed in the first place.  Please see related blog post Business Objects That Support a Parent/Child Relationship in .NET for information on how we implement the Parent/Child functionality that allows the child to reference its parent and provide the functionality needed here to remove new items from the parent list when they are cancelled.

EndEdit – Editing is complete and the changes are finalized, so we set the old version of the object that we created to backup the original data to nothing.  We also set the internal edit mode flag to false.

4) Create a new business object class (or use an existing one) and inherit BusinessObjectBase – you will now be able to use this class object in user interface components and retain commit/rollback functionality.

<serializable ()> _
Public MustInherit Class BusinessObjectBase
    Implements ComponentModel.IEditableObject
 
    Protected m_oOldVersion As BusinessObjectBase 
    Dim m_bInEditMode As Boolean
 
    ''' <summary>
    ''' The IList collection object that this object is part of.  If this object is not a child object in an IList object then Nothing is returned.
    ''' </summary>
    '<nonserialized ()> _
    Public ParentList As System.Collections.IList
 
    ''' <summary>
    ''' The parent object.  If this is a top level object then Nothing is returned.
    ''' </summary>
    Public Parent As BusinessObjectBase ' BusinessObjectBase
 
    ''' <summary>
    ''' Is the current instance of the business object a new instance that has not been committed to the database.
    ''' This is usually implemented by checking the value of an identifier property that would relate to a primary key in the database
    ''' </summary>
    Public MustOverride Function IsNew() As Boolean
 
    ''' <summary>
    ''' Method invoked by databound controls to signal that the object is going into edit mode (begin editing)
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub BeginEdit() Implements System.ComponentModel.IEditableObject.BeginEdit
        'store the old version of this object - so if we need to undo we will revert back to it
        If m_bInEditMode = False Then
            m_bInEditMode = True
            If m_oOldVersion Is Nothing Then 'this is only nothing when there are not pending changes to the object
                m_oOldVersion = Me.MemberwiseClone()
            End If
        End If
    End Sub
 
    ''' <summary>
    ''' Method invoked by databound controls to signal a cancel/rollback of the changes that have been made
    ''' to the object since editing began.  If editing an existing object then changes are rolled back.  
    ''' If editing a new object this new instance is removed from it's parent list.
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub CancelEdit() Implements System.ComponentModel.IEditableObject.CancelEdit
        Dim o As BusinessObjectBase
        o = Me
        If o.IsNew Then
            'this is a new object that hasn't been committed to the database
            o.ParentList.Remove(Me)
        Else
            'revert back to previous
            RevertObject()
        End If
        m_bInEditMode = False
    End Sub
 
    ''' <summary>
    ''' The property values of the current instance of this object are 
    ''' reverted back to their original values as of the BeginEdit method call.
    ''' Reflection is used to revert from the old version of the class to 
    ''' this instance by cycling through each property and setting the value
    ''' of this instances property equal to the value of the old version of the objects 
    ''' property value.
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub RevertObject()
        Dim t As Type = Me.GetType
        Dim p As Reflection.PropertyInfo
        For Each p In t.GetProperties()
            If p.CanWrite And p.CanRead Then
                'get original property value
                Dim oValue As Object = Nothing
                oValue = p.GetValue(m_oOldVersion, Nothing)
                'set value of this instance equal to original value 
                p.SetValue(Me, oValue, Nothing)
            End If
            'Next
        Next
    End Sub
 
    ''' <summary>
    ''' Editing of this object has been completed
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub EndEdit() Implements System.ComponentModel.IEditableObject.EndEdit
        m_bInEditMode = False
        m_oOldVersion = Nothing
    End Sub
 
End Class

Please see related article Business Objects That Support a Parent/Child Relationship in .NET for more information about creating the Parent/Child relationship between business objects and parent lists.

Please let me know if you have any questions and I’d be happy to address them. If you have any comments on how I could improve this type of article in the future plase let me know.

Advertisements

About dandouglas
Dan Douglas is based in Toronto, Ontario, Canada and is a professional independent Software Consultant and an experienced and proven subject matter expert, decision maker, and leader in the area of Software Development and Architecture. His professional experience represents over 15 years of architecting and developing highly successful large scale solutions. Dan also believes that properly empowering teams with trust and responsibility yields the greatest results. | For inquiries about Dan's software consulting practice, please see the contact page.  Dan has also built up a network of highly successful and professional Software Developers and Architects who are highly respected and can be called upon and used in conjunction with his own consulting practice to handle the largest of consulting projects.

6 Responses to Add Reusable (Inheritable) Commit/Rollback (Cancel Edit) Functionality To Your .NET Business Objects by Implementing IEditableObject

  1. Pingback: Business Objects That Support a Parent/Child Relationship in .NET « IT is Possible – Dan Douglas is blogging

  2. Jon says:

    10 hours hair pulling and I find this…you are my hero!

    My situation is this if it helps anyone.

    I have a List

    Bindingsource.DataSource = List

    I have a grid populated by the bindingsource and a edit modal form. The bindingsource gets passed to the form so I can databind textboxes. The DataSourceUpdateMode is set to OnPropertyChanged. MyClass detects dirty columns ie/not sent to the DB. If Dirty then a message appears saying do you want to Save. If they click No I call bindingsource.CancelEdit and it gets reverted thanks to the code above!

  3. This is a beneficial blog posting, im delighted I ran across it. Ill be back down the track to check out other posts that you have on your blog.

  4. dandouglas says:

    I really appreciate the feedback…

  5. mattgenton says:

    hi to all at dandouglas.wordpress.com i thought i had sent this newyears eve but it didnt send so i have sent it again good luck for 2012 to you all
    – gentas

  6. Johnf0 says:

    Hi there! Would you mind if I share your blog with my myspace group? There’s a lot of people that I think would really appreciate your content. Please let me know. Many thanks befdefffebga

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s