Possible CSLA .NET 3.5 change (Equals())

Possible CSLA .NET 3.5 change (Equals())

Old forum URL: forums.lhotka.net/forums/t/3893.aspx


RockfordLhotka posted on Tuesday, November 13, 2007

Due to the ugly issue with the way WPF handles equality between objects (which I believe is fundamentally broken, but unfortunately isn't likely to change), I am considering making a breaking change to CSLA .NET in version 3.5.

The CSLA base classes currently override System.Object.Equals() to provide the concept of logical equality, so two objects that return the same GetIdValue() value are considered to be equal.

WPF treats Equals() like ReferencesEquals() however (even though they aren't the same thing) and so it gets confused when any two object instances claim to be equal to each other. This confusion expresses itself as a flaw in data binding, where the UI doesn't rebind to a new object if it is "equal" to the one already bound to the UI.

So I am considering removing the Equals() override in the CSLA base classes. This would effectively make Equals() be like ReferenceEquals(), because that's the default behavior of Equals(). Each object would be equal only to itself.

If your application relies on logical equality, it would break. Now the reality is that most applications would be unaffected, because most people don't actually care a lot about equality. Basic operations using lists and data binding don't really care about logical equality, as long as objects can be uniquely identified.

You would likely be affected only if you explicitly compare for equality between objects in your business code, and do desire two object instances with the same key value from GetIdValue() to be considered as equal.

So I am starting this thread to get a sense for how many people would be affected, and thus to determine whether a more complex solution/workaround is required.

William replied on Tuesday, November 13, 2007

In my opinion, the code "if (obj1.ID == obj2.ID)" is more clearer then "if (obj1.Equals(obj2))". Thus, I think it is ok this make this breaking change although it's no longer follow .NET semantics (as what WPF did).

However, object equality may have its use if its semantics can be properly spelled out. Just a suggestion here. What if CSLA has its own ICslaComparer, for example:

public interface ICslaComparer<T>
{
    bool ValueEquals(T obj);
}


Then, BusinessBase<T> actually implements ICslaComparer<T>. At the end, instead of using Equals(), which I think ValueEquals() tells what it does directly from its name.

Thanks.

tetranz replied on Tuesday, November 13, 2007

Rocky, would this break the use of the IndexOf  method of a collection?

Ross

RockfordLhotka replied on Tuesday, November 13, 2007

tetranz:
Rocky, would this break the use of the IndexOf  method of a collection?

No, because the object would still be equal to itself, so IndexOf() would continue to work

x = new Customer();
list.Add(x);
int pos = list.IndexOf(x);

That works fine either way, because x==x using all forms of equality.

What would NOT work is this:

x = new Customer();
list.Add(x);
y = x.Clone();
int pos = list.IndexOf(y);

Even though logically you can argue that y==x, using System.Object.Equals() they are not equal. This exemplifies the type of break you'd get in your code - and I think this is relatively rare?

Angel replied on Wednesday, November 14, 2007

But this will affect windows forms in things like combos.

Imagine a combo with a ReadOnlyList that binds a class with a property of combo's readonlylist item type. I Think that the combo syncronizes his items comparing with equals function.

 

 

 

RockfordLhotka replied on Wednesday, November 14, 2007

Angel:

But this will affect windows forms in things like combos.

Imagine a combo with a ReadOnlyList that binds a class with a property of combo's readonlylist item type. I Think that the combo syncronizes his items comparing with equals function.

No, it should have no impact. A ComboBox bound to a NVL or other list has two parts: the key and the value. The value is displayed to the user, the key is used to index back into the list to find the item. At no point is Equals() used to locate the item in the list - the key value is used to do that.

RobKraft replied on Thursday, July 24, 2008

We just implemented 3.5 (3.5.1 actually), and the combobox problem has occurred for us.  We have standard windows forms ComboBox classes that are not bound, but we add items to them from a ReadOnlyListBase.  Later in the code, we try:

comboBox123.SelectedItem = MyReadOnlyListBase.GetListItem(1);

 and this no longer works.  The SelectedItem property is not set to the item returned by GetListItem because it no longer thinks they are equal.

I am investigating overriding Equals in our custom classes derived from the CSLA classes - but this is not as simple as I first thought it would be.

RobKraft replied on Thursday, July 24, 2008

The light bulb always turns on for me right after I post.

I was attempting to override the Equals in the collection classes, not the object classes.  Once I overrode Equals and GetHashCode in our object class (using the Equals and GetHashCode from CSLA 3.0 with slight modifications), our problems appear to be resolved.  Thanks for this thread!

 

RockfordLhotka replied on Thursday, July 24, 2008

That Equals() thing is just a mess – darn the WPF guys for changing the definition of the function…

 

You should be able to implement Equals() like it was in CSLA 3.0 in your custom base classes. The GetIdValue() method still exists, it is just not required anymore. But if your classes implement GetIdValue() as a matter of practice, then that Equals() override should function as it did before.

 

Rocky

 

From: RobKraft [mailto:cslanet@lhotka.net]
Sent: Thursday, July 24, 2008 3:30 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] Possible CSLA .NET 3.5 change (Equals())

 

We just implemented 3.5 (3.5.1 actually), and the combobox problem has occurred for us.  We have standard windows forms ComboBox classes that are not bound, but we add items to them from a ReadOnlyListBase.  Later in the code, we try:

comboBox123.SelectedItem = MyReadOnlyListBase.GetListItem(1);

 and this no longer works.  The SelectedItem property is not set to the item returned by GetListItem because it no longer thinks they are equal.

I am investigating overriding Equals in our custom classes derived from the CSLA classes - but this is not as simple as I first thought it would be.



paupdb replied on Thursday, July 24, 2008

I assume Silverlight - being a nephew of WPF - has the same Equals implementation as WPF?

RockfordLhotka replied on Thursday, July 24, 2008

I haven’t tested that, but that seems like a good assumption.

 

Rocky

 

From: paupdb [mailto:cslanet@lhotka.net]
Sent: Thursday, July 24, 2008 5:51 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] RE: Possible CSLA .NET 3.5 change (Equals())

 

I assume Silverlight - being a nephew of WPF - has the same Equals implementation as WPF?


ajj3085 replied on Wednesday, November 14, 2007

I suppose that if people needed the old behavior, they could just put the Equals override back into their own subclass of BB or ROB.

OTOH, how many Csla-ers will be using only WPF?  Probably not many compared to Forms / Asp.Net, at least not for a while.

I'll have to check my code to see if this ever came up.

JoeFallon1 replied on Wednesday, November 14, 2007

If I pass a parameter named "key" to a method:    key As Object

Where key is a numeric value(Int16, 32,64) or a String,

And then use this code:

If someBO.GetIdValue.Equals(key) Then

Will there be an issue?

Joe

RockfordLhotka replied on Wednesday, November 14, 2007

JoeFallon1:

If I pass a parameter named "key" to a method:    key As Object

Where key is a numeric value(Int16, 32,64) or a String,

And then use this code:

If someBO.GetIdValue.Equals(key) Then

Will there be an issue?

Joe

There shouldn't be any issue. I'm not proposing getting rid of GetIdValue(). I am just proposing that it wouldn't be used for the Equals() (and related GetHashCode()) overrides. To turn that around, it would only be used for the default ToString() override, and in any code like you describe where you do call GetIdValue() directly.

tetranz replied on Wednesday, November 14, 2007

This would break some of my code and necessitate some recoding but not a big deal if its for the greater good.

I have an observer pattern thing where my "readonly" lists of info objects (mostly for grids) get updated when some events occur. I usually know the id but need the index so I tend to create an throwaway info object with everything blank except the id and use that with IndexOf.

Since I have my own base objects between CSLA and my app I think a quick fix will be to create my own IndexOf in my ReadOnlyList base to hide the underlying IndexOf. I'm only doing Windows Forms for now.

Ross

RockfordLhotka replied on Wednesday, November 14, 2007

Yes, that sounds like it would be broken.

 

A quicker fix (if you don’t plan to use WPF) would be to override Equals() in your base classes just like CSLA does today – then you’d continue to get exactly the same behavior as you already have.

 

Rocky

 

 

From: tetranz [mailto:cslanet@lhotka.net]
Sent: Wednesday, November 14, 2007 3:23 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] Possible CSLA .NET 3.5 change (Equals())

 

This would break some of my code and necessitate some recoding but not a big deal if its for the greater good.

I have an observer pattern thing where my "readonly" lists of info objects (mostly for grids) get updated when some events occur. I usually know the id but need the index so I tend to create an throwaway info object with everything blank except the id and use that with IndexOf.

Since I have my own base objects between CSLA and my app I think a quick fix will be to create my own IndexOf in my ReadOnlyList base to hide the underlying IndexOf. I'm only doing Windows Forms for now.

Ross


DavidDilworth replied on Friday, November 16, 2007

FWIW, I don't think that would be a problem for us.  And we'd be willing to accept such a small breaking change anyway if we were to upgrade.

Who would have thought that equality was such a tricky subject?  We had a problem recently due to the fact that we had used the equality operator (i.e. ==) instead of the Equals() method.  The "expected" results you get for value types and reference types are different!

fernandoacorreia replied on Saturday, November 17, 2007

It seems to me a good thing to modify the current Equals() implementation.

We could use Equals() to mean that two objects contain the same state. The default implementation would only work if the two objects were in fact the same instance. But the developer could override it to test for full value equality.

For instance, object A with Id=1, Name="One" would be equal to object B with Id=1, Name="One" but different from object C with Id=1, Name="Two".

The benefit of this would be that Equals() could be used to verify whether the state of an object that is being assigned is different from the state of a previous copy. If the state of the two objects is the same (e.g. the objects are "equal"), the method in question could avoid some costly operation.

The concept that two objects refer to the same database row, even though they may have in fact different state, seems to me like a business concern that could be addressed by a specialized business method.

To this method, comparing objects A and C from the example above would result "true", even though they're not equal.

robert_m replied on Sunday, November 18, 2007

Doesn't seem like big trouble to me. But it would be nice if you could make it configurable (using some global setting or a #define or whatever... so we could get the old behavior without messing with CSLA code or introducing intermediary base classes ?

RockfordLhotka replied on Sunday, November 18, 2007

I could do that sort of configuration, but I’m really not sure it is worth it. So far it sounds like I will only break one person, and I’m pretty sure they are doing what I recommend by having custom base classes J

 

Rocky

 

 

From: robert_m [mailto:cslanet@lhotka.net]
Sent: Sunday, November 18, 2007 7:40 AM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] Possible CSLA .NET 3.5 change (Equals())

 

Doesn't seem like big trouble to me. But it would be nice if you could make it configurable (using some global setting or a #define or whatever... so we could get the old behavior without messing with CSLA code or introducing intermediary base classes ?



rsbaker0 replied on Friday, October 10, 2008

Well, I just upgraded to CSLA 3.5 and this blew me right out of the water...

:(

Is there any harm in me implementing the override myself in my common BusinessBase<T> derived class?

 

#if VALUEEQUALITY
        /// <summary>
        /// Implement equality based on primary key
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals(object obj)
        {
            if (obj is T)
            {
                return Object.Equals(GetIdValue(), ((T)obj).GetIdValue());
            }
            else
                return false;
        }
#endif

Among other things, I do things like updating objects in lists on the screen in place after a transaction versus refetching the list, and since a saved or refetched individual object will no longer be "equal" to the stale list value you're trying to replace, it no longer finds the objects in the list?

RockfordLhotka replied on Friday, October 10, 2008

The reason I made this change was because the two strategic UI technologies offered by Microsoft aren’t compatible with logical equality.

 

The only issue you’ll encounter is that you won’t be able to use WPF or Silverlight, because they won’t like your Equals() override. So you’ll be stuck in Windows Forms and/or ASP.NET.

 

Rocky

rsbaker0 replied on Friday, October 10, 2008

If the problem areas in WPF and Silverlight are depending on reference equality, why don't they explicitly test for it using ReferenceEquals instead of using Equals?

I think the impact of this change is more insidious than may appear at first glance. Any code that was previously storing any BusinessBase object in a collection could previously determine if an object was already in the collection using built-in methods without worry about reference equality. Now every place where these methods would previously return true, and index, etc, they will indicate the object is not present.

Object caches, for example, may not work any more. Every Save() of an object renders a prior reference invalid, so what was previously a cache hit will now potentially be a miss, etc.

RockfordLhotka replied on Saturday, October 11, 2008

I can’t explain why WPF didn’t use ReferenceEquals() like I would have expected… All I can do is live in the world they create, for better or worse.

 

Rocky

 

rsbaker0 replied on Saturday, October 11, 2008

^^^^

Perhaps you could use your vast influence to help them see the error of their ways... :)

 

Copyright (c) Marimer LLC