23

I need to know how do you check if an object is changed. Basically I need something like a property that is named TrackChanges, when I set it true once and if any data within this object is "changed", a method on the same object (IsObjectChanged) can return true.

Did you ever needed such a thing and how did you solve it? I do not want to invent the wheel if there is already a best-practice for such scenario?

I was thinking to clone the object before I call TrackChange=true, in its setter. And when I call IsObjectChanged() By using reflection I will compare all the public field values of it with the cloned copy. I am not sure if it is a good way.

Any advices?

thanks, burak ozdogan

Moayad Mardini
  • 7,271
  • 5
  • 41
  • 58
pencilCake
  • 51,323
  • 85
  • 226
  • 363
  • possible duplicate of [What is the best way to tell if an object is modified?](http://stackoverflow.com/questions/34809/what-is-the-best-way-to-tell-if-an-object-is-modified) – mathieu Aug 16 '12 at 12:29

6 Answers6

16

When I need to track changes to properties on my objects for testing I hook an event handler on the objects PropertyChanged event. Will that help you? Then your tests can do whatever action they want based on the change. Normally I count the number of changes, and add the changes to dictionaries, etc.

To achieve this your class must implement the INotifyPropertyChanged interface. Then anyone can attach and listen to changed properties:

public class MyClass : INotifyPropertyChanged { ... }

[TestFixture]
public class MyTestClass
{
    private readonly Dictionary<string, int> _propertiesChanged = new Dictionary<string, int>();
    private int _eventCounter; 

    [Test]
    public void SomeTest()
    {
        // First attach to the object
        var myObj = new MyClass(); 
        myObj.PropertyChanged += SomeCustomEventHandler;
        myObj.DoSomething(); 
        // And here you can check whether the object updated properties - and which - 
        // dependent on what you do in SomeCustomEventHandler. 

        // E.g. that there are 2 changes - properties Id and Name changed once each: 
        Assert.AreEqual(2, _eventCounter); 
        Assert.AreEqual(1, _propertiesChanged["Id"]);
        Assert.AreEqual(1, _propertiesChanged["Name"]);
    }

    // In this example - counting total number of changes - and count pr property. 
    // Do whatever suits you. 
    private void SomeCustomEventHandler(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        var property = e.PropertyName;
        if (_propertiesChanged.ContainsKey(property))
            _propertiesChanged[property]++;
        else
            _propertiesChanged[property] = 1;

        _eventCounter++;
    }
}
stiank81
  • 25,418
  • 43
  • 131
  • 202
  • Nice! Bur questions: 1) What is considered as "change?" In msdn it says it is thrown when the setter is called. So I still need to check if it is different than the preceding value, right? 2) What if a property of this object is a reference to another type of object, and if that object's property is changed, I think the case becomes much more complicated. something like: myPerson.Address.Ctiy = "aDifferentCityName" – pencilCake Jan 15 '10 at 12:59
  • A change is a PropertyChanged event. Implementing the INotifyPropertyChanged interface you need to trigger the events yourself. As you control this yourself you can basically choose if you want a change to same value to be considered a change. I don't. – stiank81 Jan 15 '10 at 13:03
  • If MyObject has a property that refers another object and you want PropetyChanged events when this one is changed too MyObject needs to hook a PropertyChanged event on its referenced object and trigger a changed event to pass it on. It sure gets more complicated - It might be hard at first, but it's not really that complicated when you understand it. Good luck! – stiank81 Jan 15 '10 at 13:06
  • Specifically for you example - Your address class needs to implement INotifyPropertyChanged as well, and trigger events when its properties changed. You then have two options. Either you bind directly to the Address object through MyClass, and MyClass doesn't need to handle changed events. Or - if it should care - MyClass needs to hook an event handler to its reference address object, and when an event is triggered the event handler on MyClass should trigger a PropertyChanged event on for the property Address - e.g.. However change of property Address probably means the reference was changed. – stiank81 Jan 15 '10 at 13:15
4

There are two parts to this. Events for change notification are one piece, but maintaining history is another important piece. Entity Framework does this too (as does LINQ to SQL), and I've implemented this in my own code as well. At minimum, you keep a flag for a member to say that it has changed. Depending on your requirements, you may keep the original value as well. This usually becomes the task of a separate object. Entity Framework keeps its change tracking in a separate object (EntityState, if I remember correctly).

In my own code, I developed a "DataMember" class that not only held the values, but also kept change flag, null status, and various other useful things. These DataMembers were private members in an Entity class, and the Entity provided properties that exposed the data as simple data types. The property get and set methods interacted with the DataMember to "do the right thing", but the DataMember did change tracking. My Entity class inherited from an "EntityBase" class that provided methods to check for change at the entity level, accept changes (reset change flags), etc. Adding change notification will be the next thing I do, but having a DataMember class for individual data elements, and an EntityBase to own the change notification event handler, will simplify this a lot.

EDITED TO ADD:

Now that I'm at work, I can add some code samples. Here's the interface definition for my DataMember class:

public interface IDataMember<T> : IDataMember
{
    T Value { get; set; }

    T Get();

    void Set(T value);
}

public interface IDataMember
{
    string FieldName { get; set; }
    string OracleName { get; set; }
    Type MemberType { get; }
    bool HasValue { get; set; }
    bool Changed { get; set; }
    bool NotNull { get; set; }
    bool PrimaryKey { get; set; }
    bool AutoIdentity { get; set; }
    EntityBase Entity { get; set;}

    object GetObjectValue();

    void SetNull();
}

Here's a typical property in an entity class:

private DataMember<bool> m_Monday;

public bool? Monday
{
    get
    {
        if (m_Monday.HasValue)
            return m_Monday.Get();
        else
            return null;
    }
    set
    {
        if (value.HasValue)
            m_Monday.Set(value.Value);
        else
            m_Monday.SetNull();
    }
}

Note that the DataMember can support properties as nullable, or not.

Constructor code to add a DataMember:

    m_Monday = new DataMember<bool>("Monday");
    Members.Add(m_Monday);
Cylon Cat
  • 7,111
  • 2
  • 25
  • 33
3

Burak ,

You might take a look at the Entity Framework or the other framework of Microsoft. You can see events like PropertyChanging or PropertyChanged.

Take a look at the generated code.

You might take a look at the NHibernate code as well , but since the code base is so huge, better to look at the Microsoft ORM generators..

Bahadir Cambel
  • 422
  • 5
  • 12
2

Implement and use the interface INotifyPropertyChanged. A cool way to do that without string literals is here.

Benjamin Podszun
  • 9,679
  • 3
  • 34
  • 45
1

Why don't you create a List and put your first object in, then you can compare it to the current object by using a simple comparison.

As above you can use INotifyPropertyChanged to see what properties have changed in your object.

Tony The Lion
  • 61,704
  • 67
  • 242
  • 415
  • 1
    A simple comparison would require the creation of a (deep) copy of your object. That's costly both in memory and time. – Dirk Vollmar Jan 15 '10 at 13:01
0

Instead of creating a property you should be creating an event and call it something like OnChanged.

Jacobs Data Solutions
  • 4,850
  • 4
  • 33
  • 38