Binding problem

Posts   
1  /  2
 
    
daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 18-Mar-2007 01:39:44   

I'm glad you finally find a workaround for the issue. wink

As an aside, is there an alternative way to get the Type object for System.ComponentModel.Component?

As there are other alternatives, I think that one is good.

David Elizondo | LLBLGen Support Team
simmotech
User
Posts: 1024
Joined: 01-Feb-2006
# Posted on: 09-Jan-2013 17:30:41   

Well guess who has just come across the same problem...confused I'm using DevExpress Grids/Controls but the top of the stack traces are the same.

I spent hours hacking about with BindingContexts before I found this thread.

Now, although Microsoft made a mistake with caching the property descriptors etc., I can't help but think it is exacerbated by the Entity implementation of Equals just comparing IDs.

What was the reasoning for implementing it this way? What would break if I changed it? How does ObjectID and/or ActiveContext get used?

I am think along the lines of overriding Equals to only match on Reference Equality or to use ObjectID since, in my case, I am cloning the entity from the collection (and then showing it in a popup form), and I will have the opportunity to change the ObjectID

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 09-Jan-2013 17:56:55   

simmotech wrote:

Well guess who has just come across the same problem...confused I'm using DevExpress Grids/Controls but the top of the stack traces are the same.

I spent hours hacking about with BindingContexts before I found this thread.

Now, although Microsoft made a mistake with caching the property descriptors etc., I can't help but think it is exacerbated by the Entity implementation of Equals just comparing IDs.

What was the reasoning for implementing it this way?

Equality of entity instances is really about whether their uniquely identifying attributes (PKs) are equal. Entity instances == the data, not the container (object). This thus means that ID field values are compared. If they're equal, the entity instances are equal. There's no other way, as the entity instances represent the same entity in the DB (the PK values are equal).

What would break if I changed it?

I don't know how you could change it and still get the same equality functionality.

How does ObjectID and/or ActiveContext get used?

ObjectID is the ID of the container, the object, in which the entity instance is stored in. you can store multiple instances of the same entity in multiple containers (so they'll have different objectid's) but the actual entity is the same, e.g. Customer with ID 10.

The context is for uniquing, which means that if you load 2 or more times the same entity instance (e.g. customer with ID 10), without the context you'll get 2 or more different entity class instances (container), with the same data. The context makes it possible to get back the same container for the entity instance, so you re-use the same entity class object, so there's just 1 entity class object (container) loaded for that context.

I am think along the lines of overriding Equals to only match on Reference Equality or to use ObjectID since, in my case, I am cloning the entity from the collection (and then showing it in a popup form), and I will have the opportunity to change the ObjectID

You confuse entity class instance with entity instance. These are not the same, see above. To facilitate 'cancel' after entity edits in your situation, you could also use the SaveFields feature and rollback the fields saved if the user presses cancel at the popup screen. No cloning needed.

Frans Bouma | Lead developer LLBLGen Pro
simmotech
User
Posts: 1024
Joined: 01-Feb-2006
# Posted on: 10-Jan-2013 12:55:33   

It is a matter of interpretation I suppose.

"The definition of what constitutes an object's value is up to the implementer of the type" - MSDN

If I have entity instances A and B and both have the same PK value but one has a particular field set as "A" and one set as "B" then although A.Equals(B) is true, they are not equal in the sense that I could arbitrarily choose one or the other and save it to the database.

But I have thought about this and think your way is the only practical way - comparing all the fields is overkill really.

I was a bit concerned about an XML comment which said that the entities override Equals() but did not override GetHashCode() but this seems not to be the case and GetHashCode does actually match Equals().

Anyway, the net result that no matter how I create a duplicate entity (loading independently from the database or cloning) then as soon as I use any form of binding on it, then the grid that is bound to its 'counterpart' gets a ListChanged event and buggers up all my careful value caching! Crazy but true and that is down to Microsoft's binding and property descriptors.

Whilst saving and rolling back fields might work in some circumstances, it won't work easily here. My app has many workitems which are all independent of one another within a shell form (imagine each displayed in a different tab). If I have two bank accounts tasks, each showing their transactions in their own grid and I now edit a transaction which is transferring money between them and change the Amount field and save. The source Bank Account Task doesn't (and shouldn't) know whether there is a Task showing the destination Bank Account - all it can do is broadcast a global event with details about the entity that has changed and it is up to the listener method within the task to decide whether the information is pertinent to it and take the appropriate action if it is. Of course several listening tasks might want to deal with the entity in the global event and so they can either use the ID and reload from the database or clone the item and use it directly. I think this is better than using the original entity from the grid on the edit form since changing the fields will be seen in the source grid but not in any other relevant View and also it may have child entities which are not as easy to revert. Much better to clone the entity and throw it away if the change become unwanted.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 10-Jan-2013 17:16:41   

simmotech wrote:

It is a matter of interpretation I suppose.

"The definition of what constitutes an object's value is up to the implementer of the type" - MSDN

If I have entity instances A and B and both have the same PK value but one has a particular field set as "A" and one set as "B" then although A.Equals(B) is true, they are not equal in the sense that I could arbitrarily choose one or the other and save it to the database.

But I have thought about this and think your way is the only practical way - comparing all the fields is overkill really.

Your example really is what happens in concurrency problem situations: user A has altered customer row C and user B did that too. Who wins? simple_smile The in-memory entity instances are really in-memory mirrors of the real instance, which is in the central storage (if you're having a 100-node DB cluster, then that still files for the central storage).

Using the PK value as ID, you can alter the in-memory mirror, and persist the changes as 'changes to be made' on the real instance to the DB. The ID dictates which real instance is altered. So you can have 100 in-memory mirrors of the same entity instance (e.g. customer with ID 10), all have different fields altered, but the same PK value (as it's the same entity instance), they're really representations of changes to be performed on the real instance, which is in the DB.

I was a bit concerned about an XML comment which said that the entities override Equals() but did not override GetHashCode() but this seems not to be the case and GetHashCode does actually match Equals().

Hmm. Where exactly?

Anyway, the net result that no matter how I create a duplicate entity (loading independently from the database or cloning) then as soon as I use any form of binding on it, then the grid that is bound to its 'counterpart' gets a ListChanged event and buggers up all my careful value caching! Crazy but true and that is down to Microsoft's binding and property descriptors.

Whilst saving and rolling back fields might work in some circumstances, it won't work easily here. My app has many workitems which are all independent of one another within a shell form (imagine each displayed in a different tab). If I have two bank accounts tasks, each showing their transactions in their own grid and I now edit a transaction which is transferring money between them and change the Amount field and save. The source Bank Account Task doesn't (and shouldn't) know whether there is a Task showing the destination Bank Account - all it can do is broadcast a global event with details about the entity that has changed and it is up to the listener method within the task to decide whether the information is pertinent to it and take the appropriate action if it is. Of course several listening tasks might want to deal with the entity in the global event and so they can either use the ID and reload from the database or clone the item and use it directly. I think this is better than using the original entity from the grid on the edit form since changing the fields will be seen in the source grid but not in any other relevant View and also it may have child entities which are not as easy to revert. Much better to clone the entity and throw it away if the change become unwanted.

Clone will let you work on the same entity instance in a different entity class instance indeed, as if you've fetched it twice.

Don't overthink it though. Only keep data in-memory in edit forms which has to be saved in the same transaction. If you won't use the same transaction on changes on the entities anyway (read: it's not necessary to do so), don't keep them in-memory unnecessary, just persist the changes when you can, so it gets less complicated.

Frans Bouma | Lead developer LLBLGen Pro
simmotech
User
Posts: 1024
Joined: 01-Feb-2006
# Posted on: 11-Jan-2013 13:46:27   

Ignore what I said about the Xml comment. Brain fart on my part. stuck_out_tongue_winking_eye

The message that appeared at the bottom of the Xml comment was "'....Entity' overrides Object.Equals(object o) but does not override Object.GetHashcode()"

It was a Resharper warning that it had appended to your Xml Comment in a tooltip and I mistook it as being part of the Xml Comment.

Cheers Simon

1  /  2