Click here to Skip to main content
15,881,882 members
Articles / Programming Languages / C#
Article

Discard changes in business objects

Rate me:
Please Sign up or sign in to vote.
4.67/5 (31 votes)
15 Nov 20063 min read 95.3K   976   51   20
How to build a business object with the option to discard changes

Sample Image - IEditableObject.png

Prerequisites

In order to run the samples and follow the article, you must have Visual Studio 2005 and C# installed.

Introduction

The most convenient and advanced approach in working with data is to use business objects that encapsulate business logic. Then these objects make calls to the Data Access Layer to persist information. Business objects can be created by hand or generated by the ORM tool.

Once we have the classes ready and hydrated (data is extracted from the persistence layer), we show objects in some grids and give the user the ability to edit objects in forms. So far so good. Thanks to Microsoft for the Binding class and the ability to set bindings once and leave the .NET Framework to manage data exchange between the Form's controls and business objects.

The Problem

As a typical case, the user selects a single object from the grid and opens the form for editing. Modal form is shown and the business object's properties are bound to the form's controls. By using data binding, we are able to commit the changed data from controls to the object according to the DataSourceUpdateMode enumeration:

  • Never - Data source is never updated and values entered into the control are not parsed, validated or re-formatted.
  • OnPropertyChanged - (default) Data source is updated whenever the value of the control property changes.
  • OnValidation - Data source is updated when the control property is validated.

This gives us some control "when" the data source is updated. What if we want to discard changes?

Resolutions

At this point we have two solutions:

  1. Validate() Method

    The first one is not very elegant but is more obvious - we could use DataSourceUpdateMode.OnValidation mode to update the data source and to call Form.Validate() method only if the user wants to confirm data. If data has to be discarded Validate() method is not called. This introduces a consistency problem: what if some of controls are validated (and source is updated) but the rest are not? We, of course, ask the user to fix the information entered. But if at this point, the user decides to cancel object editing, we are in trouble because some fields are already updated.

  2. IEditableObject interface

    Fortunately there is a way to achieve this with .NET tools. This is the IEditableObject interface. This interface declares three public methods:

    • BeginEdit() - Begins an edit on an object.
    • CancelEdit() - Discards changes since the last BeginEdit call.
    • EndEdit() - Pushes changes since the last BeginEdit or IBindingList.AddNew call into the underlying object.

    There is another good news - Microsoft controls support and works well with this interface. All we have to do is to implement this interface in our business objects. This requires some work from us but gives us enough flexibility. My personal preference is to create a base class of all business objects which implements common logic including IEditableObject methods.

Generic IEditableObject implementation

Provided here is a generic implementation which could easily be added to the base class and included in existing projects. This implementation preserves values for all public properties do not keep an eye on all fields, private properties, public properties without set ancestor

First we need some key/value pair collection which will hold the original values for us.

Store original values

This is the flag that the object is in edit mode too.

C#
Hashtable props = null;

BeginEdit()

This method stores the current values for future restoration using reflection.

C#
public void BeginEdit()
{
    //exit if in Edit mode
    //uncomment if  CancelEdit discards changes since the 
    //LAST BeginEdit call is desired action
    //otherwise CancelEdit discards changes since the 
    //FIRST BeginEdit call is desired action
    //if (null != props) return;

    //enumerate properties
    PropertyInfo[] properties = (this.GetType()).GetProperties
                (BindingFlags.Public | BindingFlags.Instance);

    props = new Hashtable(properties.Length - 1);

    for (int i = 0; i < properties.Length; i++)
    {
        //check if there is set accessor
        if (null != properties[i].GetSetMethod())
        {
            object value = properties[i].GetValue(this, null);
            props.Add(properties[i].Name, value);
        }
    }
}

CancelEdit()

This method restores old values.

C#
public void CancelEdit()
{
    //check for inappropriate call sequence
    if (null == props) return;

    //restore old values
    PropertyInfo[] properties =(this.GetType()).GetProperties
        (BindingFlags.Public | BindingFlags.Instance);
    for (int i = 0; i < properties.Length; i++)
    {
        //check if there is set accessor
        if (null != properties[i].GetSetMethod())
        {
            object value = props[properties[i].Name];
            properties[i].SetValue(this, value, null);
        }
    }

    //delete current values
    props = null;
}

EndEdit()

This method just deletes stored values (and our flag).

C#
public void EndEdit()
{
    //delete current values
    props = null;
}

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer (Senior)
Bulgaria Bulgaria
Galin has entered in area of software development in end of 20th centutry with Pascal and assembler. His career went through C++, Visual Basic, ASP, InstallShiled, Wise and etc...
For last four years he has been involved in developing all kind of software in .NET.
He holds MCSD, MCAD, MCSD.NET, MCPD and MCT certificates.

Comments and Discussions

 
QuestionIEditableObject Pin
Mario forbes27-Jun-14 12:19
Mario forbes27-Jun-14 12:19 
QuestionComplex properties like Child Entities Pin
Thomas Hagström22-Jan-14 21:43
Thomas Hagström22-Jan-14 21:43 
QuestionAlternate approach? Pin
Yumashin Alex6-May-10 1:08
Yumashin Alex6-May-10 1:08 
GeneralBeginEdit EndEdit Pin
jansen842128-Dec-09 22:50
jansen842128-Dec-09 22:50 
GeneralRe: BeginEdit EndEdit Pin
jansen842110-Jan-10 4:03
jansen842110-Jan-10 4:03 
QuestionObject with a list Pin
Rui Figueiredo14-May-09 0:39
Rui Figueiredo14-May-09 0:39 
GeneralAlso Some Ideas Pin
Izzet Kerem Kusmezer8-May-08 8:00
Izzet Kerem Kusmezer8-May-08 8:00 
GeneralSimple And Super Quick Implementation Pin
Izzet Kerem Kusmezer8-May-08 7:35
Izzet Kerem Kusmezer8-May-08 7:35 
GeneralTwo feature suggestions Pin
Goljan24-Mar-08 10:52
Goljan24-Mar-08 10:52 
GeneralBeginEdit also for items selected in DataGridView Pin
Roberto Ferraris4-Sep-07 22:18
Roberto Ferraris4-Sep-07 22:18 
Generalcloning business object Pin
Primadi2-Sep-07 17:23
Primadi2-Sep-07 17:23 
GeneralNice approach .. Pin
freefighter20-Aug-07 5:14
freefighter20-Aug-07 5:14 
QuestionWhat about a generic implementation of the ContactDetails Form? Pin
Gywox29-Mar-07 5:06
Gywox29-Mar-07 5:06 
GeneralLove It Pin
Thomas Wells8-Feb-07 4:15
Thomas Wells8-Feb-07 4:15 
QuestionWhat about changes to the list itself? [modified] Pin
MithralSmith22-Nov-06 7:24
MithralSmith22-Nov-06 7:24 
AnswerRe: What about changes to the list itself? Pin
Galin Iliev [Galcho]22-Nov-06 23:23
Galin Iliev [Galcho]22-Nov-06 23:23 
Hi MithralSmith,
This is interesting issue but it is more complicated than object level discarding.
For single object discarding you have to do preserving properties only (here I am talking about non-collection values).

For collection properties it is bit different because you have to preserve two things:
1. collection objects - you have to make copy of all elements in case some is changed.
2. collection itself - in case Add/Remove operation is executed against this collection.

As we have these requirements we can start implementation. You can take a look at IClonable interface. This is what we need. We have to implement it in passed objects and collection. (I think I am going to write another article for this Smile | :) )

Once you have this done it is easy: you just call
 <br />
public override void BeginEdit()<br />
{<br />
  backupCollection = actualCollection.Clone()  // this clones all items too<br />
}<br />
<br />
public override void CancelEdit()<br />
{<br />
  actualCollection = backupCollection.Clone()  // this clones back all items too<br />
  backupCollection = null;<br />
}<br />
<br />
public override void CancelEdit()<br />
{<br />
  backupCollection = null;<br />
}<br />


Note:These are overriden methods in the subclass.

and this is all you need Smile | :)
I extended sample application above. As this is bit different than article subject you can download it from
here


Hope this helps.

Galin Iliev [MCSD, MCPD]
Senior Software Developer
Technology Services Consulting Group

GeneralRe: What about changes to the list itself? Pin
MithralSmith23-Nov-06 5:14
MithralSmith23-Nov-06 5:14 
GeneralRe: What about changes to the list itself? Pin
Galin Iliev [Galcho]27-Nov-06 23:57
Galin Iliev [Galcho]27-Nov-06 23:57 
GeneralExcellent Pin
rcardare16-Nov-06 4:24
rcardare16-Nov-06 4:24 
JokeRe: Excellent Pin
Galin Iliev [Galcho]16-Nov-06 7:38
Galin Iliev [Galcho]16-Nov-06 7:38 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.