Binding problem

Posts   
1  /  2
 
    
mikeg22
User
Posts: 411
Joined: 30-Jun-2005
# Posted on: 16-Mar-2006 19:19:39   

This problem manifests itself in binding, but I thought it might belong in the general forum because I don't know if it is a binding problem per se...

Here's my situation: I fetch an entity with PK=A and bind it to some controls using a BindingSource in one form. I fetch an entitycollection containing an entity with PK=A and bind it to a grid in another form. I am not using a context.

When I try and make a change to the entity instance in the grid, it somehow starts triggering binding events on the entity bound to the other form, even though these are completely different object instances!

Here is a stack trace of where I believe the jump from one form to another is happening:


    System.dll!System.ComponentModel.ReflectPropertyDescriptor.GetValue(object component = {CODA.Entities.EntityClasses.Employee}) + 0x57 bytes 
    System.Windows.Forms.dll!System.Windows.Forms.BindToObject.GetValue() + 0x32 bytes  
    System.Windows.Forms.dll!System.Windows.Forms.Binding.PushData(bool force) + 0x5f bytes 
    System.Windows.Forms.dll!System.Windows.Forms.BindingManagerBase.PushData(out bool success = true) + 0x50 bytes 
    System.Windows.Forms.dll!System.Windows.Forms.BindingManagerBase.PushData() + 0xe bytes 
    System.Windows.Forms.dll!System.Windows.Forms.CurrencyManager.CurrencyManager_PushData() + 0x35 bytes   
    System.Windows.Forms.dll!System.Windows.Forms.CurrencyManager.OnItemChanged(System.Windows.Forms.ItemChangedEventArgs e = {Index = 0}) + 0x50 bytes 
    System.Windows.Forms.dll!System.Windows.Forms.CurrencyManager.List_ListChanged(object sender, System.ComponentModel.ListChangedEventArgs e) + 0x3d4 bytes   
    System.Windows.Forms.dll!System.Windows.Forms.BindingSource.OnListChanged(System.ComponentModel.ListChangedEventArgs e) + 0x7b bytes    
    System.Windows.Forms.dll!System.Windows.Forms.BindingSource.ListItem_PropertyChanged(object sender, System.EventArgs e) + 0x45 bytes    
    System.dll!System.ComponentModel.PropertyDescriptor.OnValueChanged(object component, System.EventArgs e) + 0x45 bytes   
    System.dll!System.ComponentModel.ReflectPropertyDescriptor.OnValueChanged(object component, System.EventArgs e) + 0x6f bytes    
    System.dll!System.ComponentModel.ReflectPropertyDescriptor.SetValue(object component = {CODA.Entities.EntityClasses.Employee}, object value = true) + 0x137 bytes   
    Janus.Windows.GridEX.v2.dll!Janus.Windows.GridEX.GridEXColumn.a(object  = {CODA.Entities.EntityClasses.Employee}, Janus.Windows.GridEX.Internal.JNSCS  = {Janus.Windows.GridEX.Internal.JNSCS}, object  = true) + 0x3d7 bytes   
    Janus.Windows.GridEX.v2.dll!Janus.Windows.GridEX.GridEXColumn.a(Janus.Windows.GridEX.Internal.JNSBF  = {Janus.Windows.GridEX.Internal.JNSBF}, object  = true) + 0x41 bytes  
    Janus.Windows.GridEX.v2.dll!Janus.Windows.GridEX.GridEXCell.Value.set(object value = true) + 0x192 bytes    
    Janus.Windows.GridEX.v2.dll!Janus.Windows.GridEX.GridEX.g(bool  = true) + 0xf3c bytes   
    Janus.Windows.GridEX.v2.dll!Janus.Windows.GridEX.GridEX.ai() + 0x1e bytes   
    Janus.Windows.GridEX.v2.dll!Janus.Windows.GridEX.GridEX.ao() + 0x111 bytes  
    Janus.Windows.GridEX.v2.dll!Janus.Windows.GridEX.GridEX.UpdateData() + 0x19 bytes

The jump from one form to the other happens, I think, when the code goes from using the GridEx to using the Forms.BindingSource.

I believe this may have something to do with the implementation of GetHashCode in the entities and Equals(...) in the EntityFields2 class, which just uses PKs and field values to determine equivalence. My very uneducated guess is that the ComponentModel classes are caching objects using a hashtable, and my 2 entity instances are getting put into the same bucket. Of course it may be something else entirely, but thats the best I can come up with right now... confused

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 18-Mar-2006 09:30:53   

Could you try to bind the collection directly to the gridex, instead of using a bindingsource? THis is because gridexv2 isn't written for .net 2.0 specifically.

Frans Bouma | Lead developer LLBLGen Pro
mikeg22
User
Posts: 411
Joined: 30-Jun-2005
# Posted on: 20-Mar-2006 17:53:42   

I tried binding directly to the entitycollection and it still has the same problem. Here's the stack trace with no bindingsource:


    System.dll!System.ComponentModel.ReflectPropertyDescriptor.GetValue(object component = {CODA.Entities.EntityClasses.Employee}) + 0x57 bytes 
    System.Windows.Forms.dll!System.Windows.Forms.BindToObject.GetValue() + 0x32 bytes  
    System.Windows.Forms.dll!System.Windows.Forms.Binding.PushData(bool force) + 0x5f bytes 
    System.Windows.Forms.dll!System.Windows.Forms.BindingManagerBase.PushData(out bool success = true) + 0x50 bytes 
    System.Windows.Forms.dll!System.Windows.Forms.BindingManagerBase.PushData() + 0xe bytes 
    System.Windows.Forms.dll!System.Windows.Forms.CurrencyManager.CurrencyManager_PushData() + 0x35 bytes   
    System.Windows.Forms.dll!System.Windows.Forms.CurrencyManager.OnItemChanged(System.Windows.Forms.ItemChangedEventArgs e = {Index = 0}) + 0x50 bytes 
    System.Windows.Forms.dll!System.Windows.Forms.CurrencyManager.List_ListChanged(object sender, System.ComponentModel.ListChangedEventArgs e) + 0x3d4 bytes   
    System.Windows.Forms.dll!System.Windows.Forms.BindingSource.OnListChanged(System.ComponentModel.ListChangedEventArgs e) + 0x7b bytes    
    System.Windows.Forms.dll!System.Windows.Forms.BindingSource.ListItem_PropertyChanged(object sender, System.EventArgs e) + 0x45 bytes    
    System.dll!System.ComponentModel.PropertyDescriptor.OnValueChanged(object component, System.EventArgs e) + 0x45 bytes   
    System.dll!System.ComponentModel.ReflectPropertyDescriptor.OnValueChanged(object component, System.EventArgs e) + 0x6f bytes    
    System.dll!System.ComponentModel.ReflectPropertyDescriptor.SetValue(object component = {CODA.Entities.EntityClasses.Employee}, object value = true) + 0x137 bytes   
    Janus.Windows.GridEX.v2.dll!Janus.Windows.GridEX.GridEXColumn.a(object  = {CODA.Entities.EntityClasses.Employee}, Janus.Windows.GridEX.Internal.JNSCS  = {Janus.Windows.GridEX.Internal.JNSCS}, object  = true) + 0x3d7 bytes   
    Janus.Windows.GridEX.v2.dll!Janus.Windows.GridEX.GridEXColumn.a(Janus.Windows.GridEX.Internal.JNSBF  = {Janus.Windows.GridEX.Internal.JNSBF}, object  = true) + 0x41 bytes  
    Janus.Windows.GridEX.v2.dll!Janus.Windows.GridEX.GridEXCell.Value.set(object value = true) + 0x192 bytes    
    Janus.Windows.GridEX.v2.dll!Janus.Windows.GridEX.GridEX.g(bool  = true) + 0xf3c bytes   
    Janus.Windows.GridEX.v2.dll!Janus.Windows.GridEX.GridEX.ai() + 0x1e bytes   
    Janus.Windows.GridEX.v2.dll!Janus.Windows.GridEX.GridEX.ao() + 0x10e bytes  
    Janus.Windows.GridEX.v2.dll!Janus.Windows.GridEX.GridEX.UpdateData() + 0x19 bytes   


It looks like the same as before...how could you tell I was using a bindingsource to bind to the gridex?

BTW, I'm binding to controls on the other form with a bindingsource...which is obvious from the stack trace disappointed

I've set a breakpoint where EntityBase2.Equals is called from the ReflectPropertyDescriptor. Here's the stack trace:


>   SD.LLBLGen.Pro.ORMSupportClasses.dll!SD.LLBLGen.Pro.ORMSupportClasses.EntityBase2.Equals(object obj = {CODA.Entities.EntityClasses.Employee}) Line 927  C#
    mscorlib.dll!System.Collections.Hashtable.KeyEquals(object item, object key) + 0x3b bytes   
    mscorlib.dll!System.Collections.Hashtable.this[object].get(object key = {CODA.Entities.EntityClasses.Employee}) + 0x10b bytes   
    System.dll!System.ComponentModel.PropertyDescriptor.OnValueChanged(object component = {CODA.Entities.EntityClasses.Employee}, System.EventArgs e = {System.EventArgs}) + 0x21 bytes 
    System.dll!System.ComponentModel.ReflectPropertyDescriptor.OnValueChanged(object component, System.EventArgs e) + 0x6f bytes    
    System.dll!System.ComponentModel.ReflectPropertyDescriptor.SetValue(object component = {CODA.Entities.EntityClasses.Employee}, object value = true) + 0x137 bytes   
    Janus.Windows.GridEX.v2.dll!Janus.Windows.GridEX.GridEXColumn.a(object  = {CODA.Entities.EntityClasses.Employee}, Janus.Windows.GridEX.Internal.JNSCS  = {Janus.Windows.GridEX.Internal.JNSCS}, object  = true) + 0x3d7 bytes   
    Janus.Windows.GridEX.v2.dll!Janus.Windows.GridEX.GridEXColumn.a(Janus.Windows.GridEX.Internal.JNSBF  = {Janus.Windows.GridEX.Internal.JNSBF}, object  = true) + 0x41 bytes  
    Janus.Windows.GridEX.v2.dll!Janus.Windows.GridEX.GridEXCell.Value.set(object value = true) + 0x192 bytes    
    Janus.Windows.GridEX.v2.dll!Janus.Windows.GridEX.GridEX.g(bool  = true) + 0xf3c bytes   
    Janus.Windows.GridEX.v2.dll!Janus.Windows.GridEX.GridEX.ai() + 0x1e bytes   
    Janus.Windows.GridEX.v2.dll!Janus.Windows.GridEX.GridEX.ao() + 0x10e bytes  
    Janus.Windows.GridEX.v2.dll!Janus.Windows.GridEX.GridEX.UpdateData() + 0x19 bytes   

If .Net is keeping some kind of binding hashtable behind the scenes, I can see this causing a big problem, as 2 different entities with the same field values will end up in the same hash bucket.

mikeg22
User
Posts: 411
Joined: 30-Jun-2005
# Posted on: 21-Mar-2006 00:25:45   

It looks like this problem is something else entirely. I'm having the problem regardless of how many instances of an entity are opened! frowning

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 21-Mar-2006 09:00:23   

What exactly is the exception? (thanks for the stacktrace though simple_smile ). I couldn't find that in your posts.

Frans Bouma | Lead developer LLBLGen Pro
mikeg22
User
Posts: 411
Joined: 30-Jun-2005
# Posted on: 21-Mar-2006 17:17:18   

The exception is a stack overflow caused by a loop condition triggered by one of my bound properties being read.

Just to update, this loop condition is what caused me to notice the problem, because changing a property in the grid-bound object would trigger the read of a property in the form-bound object). So, I am still seeing the behavior described before...changing a property on the grid-bound object triggers a binding read on the form-bound one. Completely different entities frowning

So, I guess this is still an issue!

bclubb
User
Posts: 934
Joined: 12-Feb-2004
# Posted on: 22-Mar-2006 04:17:53   

Can you post your code to bind the grid? I don't have gridex, but it may help to see how everything is setup.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 22-Mar-2006 11:20:13   

I have no clue what's going on.. disappointed . As you said, you use 2 different forms, and the currency manager which keeps everything in sync is bound to a form, so the other form uses a different currency manager so even if it uses Equals to keep track of the instances, it shouldn't matter as it's a different form...

Frans Bouma | Lead developer LLBLGen Pro
mikeg22
User
Posts: 411
Joined: 30-Jun-2005
# Posted on: 22-Mar-2006 20:54:21   

Otis wrote:

I have no clue what's going on.. disappointed . As you said, you use 2 different forms, and the currency manager which keeps everything in sync is bound to a form, so the other form uses a different currency manager so even if it uses Equals to keep track of the instances, it shouldn't matter as it's a different form...

Yeah, I feel like I'm going crazy, this is such a strange bug. Somehow the GridEx is communicating to the other CurrencyManager. I am unable to reproduce this bug in a test app, but I'm sure it is one thing I'm doing in my real app that I'm not doing in my test app...nothing obvious that I can think of.

We are going to upgrade to the .Net 2.0 version of the GridEx when it comes out, and hopefully this problem will dissapear. If it doesn't dissapear, its not that big a deal as the cases where this can happen will be very very rare (opening 2 instances of the same entity in two different forms, and making a change to one instance which doesn't affect its field values). Anyways this shouldn't pose a real problem as it just triggers a re-read from the BindingContext of the other form to the controls of the other form.

I'll let you know anything I find out.

confused

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 22-Mar-2006 21:27:54   

mikeg22 wrote:

Otis wrote:

I have no clue what's going on.. disappointed . As you said, you use 2 different forms, and the currency manager which keeps everything in sync is bound to a form, so the other form uses a different currency manager so even if it uses Equals to keep track of the instances, it shouldn't matter as it's a different form...

Yeah, I feel like I'm going crazy, this is such a strange bug. Somehow the GridEx is communicating to the other CurrencyManager. I am unable to reproduce this bug in a test app, but I'm sure it is one thing I'm doing in my real app that I'm not doing in my test app...nothing obvious that I can think of.

I'm sorry I can't be of any assistence in this. I haven't heard of this behavior before. Just for clarity, could you compare the ObjectID values (property of every llblgen pro entity) to see if they're really two different instances?

We are going to upgrade to the .Net 2.0 version of the GridEx when it comes out, and hopefully this problem will dissapear.

You could try their beta of those controls today. Build 3.0.0.13 is out since a couple of days, and it's pretty stable. You can run it side by side, so with a bit of making some csproj backups first, you can try it out to see if it happens with the newer controls as well... I'm using the beta controls in the llblgen pro v2 designer development, and it's going ok so far simple_smile

If it doesn't dissapear, its not that big a deal as the cases where this can happen will be very very rare (opening 2 instances of the same entity in two different forms, and making a change to one instance which doesn't affect its field values). Anyways this shouldn't pose a real problem as it just triggers a re-read from the BindingContext of the other form to the controls of the other form.

I'll let you know anything I find out. confused

Yes, please keep us informed, also if you run into new info which might not look that important, it might shed some light on it and perhaps solve it simple_smile

Frans Bouma | Lead developer LLBLGen Pro
mikeg22
User
Posts: 411
Joined: 30-Jun-2005
# Posted on: 22-Mar-2006 22:03:10   

The ObjectIDs are different. The BindingContext objects are also different.

I'm trying out the beta right now and I'll let you know what I find out...

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 22-Mar-2006 22:20:41   

mikeg22 wrote:

The ObjectIDs are different. The BindingContext objects are also different.

I'm trying out the beta right now and I'll let you know what I find out...

Ok. Be sure to read the upgrade directions Janus has specified. (and keep a copy of your project files to be able to roll back simple_smile )

Frans Bouma | Lead developer LLBLGen Pro
mikeg22
User
Posts: 411
Joined: 30-Jun-2005
# Posted on: 22-Mar-2006 22:31:23   

Using v3 does not make a difference. I'm completely baffled and wish I could give you more to go on. This behavior does not happen with 2 bound entities in different forms where neither one is bound to a Janus grid. This only happens when one entity is bound to a Janus grid.

It would be nice to know what the Janus GridExColumn function named 'a' does, as that appears to set off the chain of events that causes the jump to the other BindingSource...

mikeg22
User
Posts: 411
Joined: 30-Jun-2005
# Posted on: 22-Mar-2006 22:56:06   

Here's a new stack trace using v3 of the GridEx:


>   Entities.dll!CODA.Entities.EntityClasses.Employee.get_AddressCountry_Id() Line 5907 Basic
    [Native to Managed Transition]  
    [Managed to Native Transition]  
    System.dll!System.ComponentModel.ReflectPropertyDescriptor.GetValue(object component = {CODA.Entities.EntityClasses.Employee}) + 0x57 bytes 
    System.Windows.Forms.dll!System.Windows.Forms.BindToObject.GetValue() + 0x32 bytes  
    System.Windows.Forms.dll!System.Windows.Forms.Binding.PushData(bool force) + 0x5f bytes 
    System.Windows.Forms.dll!System.Windows.Forms.BindingManagerBase.PushData(out bool success = true) + 0x50 bytes 
    System.Windows.Forms.dll!System.Windows.Forms.BindingManagerBase.PushData() + 0xe bytes 
    System.Windows.Forms.dll!System.Windows.Forms.CurrencyManager.CurrencyManager_PushData() + 0x35 bytes   
    System.Windows.Forms.dll!System.Windows.Forms.CurrencyManager.OnItemChanged(System.Windows.Forms.ItemChangedEventArgs e = {Index = 0}) + 0x50 bytes 
    System.Windows.Forms.dll!System.Windows.Forms.CurrencyManager.List_ListChanged(object sender, System.ComponentModel.ListChangedEventArgs e) + 0x3d4 bytes   
    System.Windows.Forms.dll!System.Windows.Forms.BindingSource.OnListChanged(System.ComponentModel.ListChangedEventArgs e) + 0x7b bytes    
    System.Windows.Forms.dll!System.Windows.Forms.BindingSource.ListItem_PropertyChanged(object sender, System.EventArgs e) + 0x45 bytes    
    System.dll!System.ComponentModel.PropertyDescriptor.OnValueChanged(object component, System.EventArgs e) + 0x45 bytes   
    System.dll!System.ComponentModel.ReflectPropertyDescriptor.OnValueChanged(object component, System.EventArgs e) + 0x6f bytes    
    System.dll!System.ComponentModel.ReflectPropertyDescriptor.SetValue(object component = {CODA.Entities.EntityClasses.Employee}, object value = true) + 0x137 bytes   
    Janus.Data.v3.dll!Janus.Data.Global.SetNestedProperty(System.Globalization.CultureInfo cInfo = {en-US}, object dataRow = {CODA.Entities.EntityClasses.Employee}, string dataMember = "AllChangesHaveBeenReviewed", object newValue = true) + 0x13a bytes    
    Janus.Data.v3.dll!Janus.Data.Global.SetPropertyValue(System.Globalization.CultureInfo cInfo = {en-US}, object dataRow = {CODA.Entities.EntityClasses.Employee}, System.ComponentModel.PropertyDescriptor propertyDescriptor = null, string dataMember = "AllChangesHaveBeenReviewed", object newValue = true) + 0x37 bytes  
    Janus.Data.v3.dll!Janus.Data.DataHolderRow.SetValue(string dataMember = "AllChangesHaveBeenReviewed", object newValue = true) + 0x42 bytes  
    Janus.Data.v3.dll!Janus.Data.DataHolderRow.SetValue(Janus.Data.JanusFieldBase field = {Janus.Windows.GridEX.GridEXColumn}, object newValue = true) + 0xd5 bytes 
    Janus.Data.v3.dll!Janus.Data.JanusDataRow.SetValue(Janus.Data.JanusFieldBase field = {Janus.Windows.GridEX.GridEXColumn}, object newValue = true) + 0x2e bytes  
    Janus.Data.v3.dll!Janus.Data.JanusFieldBase.BaseSetValue(Janus.Data.JanusRow row = {Janus.Data.JanusDataRow}, object value = true) + 0x4e bytes 
    Janus.Windows.GridEX.v3.dll!Janus.Windows.GridEX.GridEXColumn.a(Janus.Data.JanusRow  = {Janus.Data.JanusDataRow}, object  = true) + 0xc6 bytes  
    Janus.Windows.GridEX.v3.dll!Janus.Windows.GridEX.GridEXCell.Value.set(object value = true) + 0x1a4 bytes    
    Janus.Windows.GridEX.v3.dll!Janus.Windows.GridEX.GridEX.h(bool  = true) + 0x1178 bytes  
    Janus.Windows.GridEX.v3.dll!Janus.Windows.GridEX.GridEX.an() + 0x1e bytes   
    Janus.Windows.GridEX.v3.dll!Janus.Windows.GridEX.GridEX.at() + 0x61 bytes   
    Janus.Windows.GridEX.v3.dll!Janus.Windows.GridEX.GridEX.UpdateData() + 0x4a bytes   
    CODA GUI.exe!CODA.UI.EmployeeAuditForm.CellValueChanged(Object sender = {CODA.UI.GridBase}, Janus.Windows.GridEX.ColumnActionEventArgs e = {Janus.Windows.GridEX.ColumnActionEventArgs}) Line 378 + 0x15 bytes  Basic


UpdateData is called on the GridEx (bottom of stack) which ends up triggering the Property Get_AddressCountry_ID (at the top of the stack).

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 22-Mar-2006 22:56:38   

mikeg22 wrote:

Using v3 does not make a difference. I'm completely baffled and wish I could give you more to go on. This behavior does not happen with 2 bound entities in different forms where neither one is bound to a Janus grid. This only happens when one entity is bound to a Janus grid.

It would be nice to know what the Janus GridExColumn function named 'a' does, as that appears to set off the chain of events that causes the jump to the other BindingSource...

You could ask them: support AT janusys.com. Jose and friends are often very responsive simple_smile

Frans Bouma | Lead developer LLBLGen Pro
mikeg22
User
Posts: 411
Joined: 30-Jun-2005
# Posted on: 22-Mar-2006 23:36:12   

Frans,

You aren't caching property descriptors somewhere are you? I looked through the generated code and ORMSupportClasses and can't find any place where you were, but I just wanted to make sure...

My latest theory is that the two form's binding sources are watching for change events from the same set of PropertyDescriptors. It seems, from how I understand it, that TypeDescriptor keeps cached PropertyDescriptor objects for any given type. So, when binding on the two forms, GetProperties is just returning the same set of PropertyDescriptor instances for both bindings. The shared PropertyDescriptor keeps an internal hashtable of delegates to call when the SetValue is called (in OnValueChanged of the propertydescriptor). The hashtable's key is my entity in this case. Because the different entities are equivalent as far as the hashtable is concerned, both delegates get put in the same bucket when binding happens on both forms. These delegates are both called when PropertyDescriptor.SetValue(entity, value) is called.

The only thing that makes me doubt this theory is that I can't reproduce it!

Walaa avatar
Walaa
Support Team
Posts: 14946
Joined: 21-Aug-2005
# Posted on: 23-Mar-2006 06:55:24   

Hey Mike, just out of curiosity would you post the code that do the following:

I fetch an entity with PK=A and bind it to some controls using a BindingSource in one form. I fetch an entitycollection containing an entity with PK=A and bind it to a grid in another form.

I see that was the only thing missing in this thread.

Thanks.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 23-Mar-2006 08:41:23   

mikeg22 wrote:

Frans,

You aren't caching property descriptors somewhere are you? I looked through the generated code and ORMSupportClasses and can't find any place where you were, but I just wanted to make sure...

No, I calculate them every time (which is a fast operation).

My latest theory is that the two form's binding sources are watching for change events from the same set of PropertyDescriptors. It seems, from how I understand it, that TypeDescriptor keeps cached PropertyDescriptor objects for any given type. So, when binding on the two forms, GetProperties is just returning the same set of PropertyDescriptor instances for both bindings. The shared PropertyDescriptor keeps an internal hashtable of delegates to call when the SetValue is called (in OnValueChanged of the propertydescriptor). The hashtable's key is my entity in this case. Because the different entities are equivalent as far as the hashtable is concerned, both delegates get put in the same bucket when binding happens on both forms. These delegates are both called when PropertyDescriptor.SetValue(entity, value) is called.

The only thing that makes me doubt this theory is that I can't reproduce it!

Heh simple_smile Property descriptors are objects which simply define a property, but you need an instance which implements these properties to access the property. So your theory isn't what's causing it I think.

Frans Bouma | Lead developer LLBLGen Pro
mikeg22
User
Posts: 411
Joined: 30-Jun-2005
# Posted on: 23-Mar-2006 16:18:23   

Aren't the PropertyDescriptor objects used by the binding source to actually set the values of the properties? Inside SetValue, the value is set, and listeners are notified that the value was set for a specific object. The listeners are notified because they had previously called

PropertyDescriptor.AddValueChanged(object,eventhandler)

Inside AddValueChanged, the eventhandler is added to other eventhandlers in a hashtable whose key is the object passed in (in my case the entity). So, if two different instances of an entity Equal eachother, and both use the same PropertyDescriptor, then both seperate BindingSource objects will be notified if the property is changed with PropertyDescriptor.SetValue.

I've almost given up on fixing this problem, and am trying to just learn more about binding at this point, but could you tell me where I'm wrong here?

Walaa avatar
Walaa
Support Team
Posts: 14946
Joined: 21-Aug-2005
# Posted on: 24-Mar-2006 07:46:50   

Before you give up please do me a favour and post the code that do the following:

I fetch an entity with PK=A and bind it to some controls using a BindingSource in one form. I fetch an entitycollection containing an entity with PK=A and bind it to a grid in another form.

I see that was the only thing missing in this thread.

Thanks.

mikeg22
User
Posts: 411
Joined: 30-Jun-2005
# Posted on: 08-Jun-2006 02:30:27   

Just a followup to this problem. It seems as if the TypeDescriptor is caching our entities when we databind, and the Equals of the entities is causing the two separate entity instances to be treated as the same entity, causing the cross form communication. Here's the MSDN article that describes the TypeDescriptor behavior of caching databound objects:

MSDN lib link

Look at the section "Always Call DataBindings.Clear when Disposing"

We worked around the cross-form communication problem, but this behavior ended up causing a massive memory leak as closing a form with databound entities did not result in the entities being garbage collected flushed

I am trying Microsoft's suggested solution to this "feature" right now...

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 08-Jun-2006 10:38:48   

Isn't this just a bug in the databinding code of winforms? I mean, why on earth would you keep references from a form to objects when the form is disposed? (not you, but microsoft)....

Frans Bouma | Lead developer LLBLGen Pro
mikeg22
User
Posts: 411
Joined: 30-Jun-2005
# Posted on: 08-Jun-2006 15:09:32   

Otis wrote:

Isn't this just a bug in the databinding code of winforms? I mean, why on earth would you keep references from a form to objects when the form is disposed? (not you, but microsoft)....

I guess its a bug, but it seems to be by design so I just don't know. I can see why it would be done for performance reasons, but it seems like a memory leak waiting to happen...keeping shared references to local objects when the user doesn't know about it, and putting the onus on the user to get rid of the references with TypeDescriptor.Refresh is insane confused

By the way, I've tried Microsoft's solution, but it does nothing to fix this bug in our code. I just can't seem to get our entity references out of this shared area using their ClearBindings method. I'm going to give it another shot today, but we'll see.

Outside of the obvious memory leak issue, there is still the danger of having 2 entities that are .Equal to eachother in memory that have both been databound. From what I've seen, in this situation, setting a property on one will trigger a databinding read on the other. This was causing a problem in our app because of a bug we introduced, but I don't really see how it could be a real problem other than causing confusion when trying to debug.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 09-Jun-2006 10:11:04   

mikeg22 wrote:

Otis wrote:

Isn't this just a bug in the databinding code of winforms? I mean, why on earth would you keep references from a form to objects when the form is disposed? (not you, but microsoft)....

I guess its a bug, but it seems to be by design so I just don't know. I can see why it would be done for performance reasons, but it seems like a memory leak waiting to happen...keeping shared references to local objects when the user doesn't know about it, and putting the onus on the user to get rid of the references with TypeDescriptor.Refresh is insane confused

It indeed is frowning

I didn't know TypeDescriptor cached any real instance as I have no clue why on earth it should keep a reference to an instance, it's a type descriptor!... but then again, there are so many weird things in winforms, I'm not surprised.

By the way, I've tried Microsoft's solution, but it does nothing to fix this bug in our code. I just can't seem to get our entity references out of this shared area using their ClearBindings method. I'm going to give it another shot today, but we'll see.

Outside of the obvious memory leak issue, there is still the danger of having 2 entities that are .Equal to eachother in memory that have both been databound. From what I've seen, in this situation, setting a property on one will trigger a databinding read on the other. This was causing a problem in our app because of a bug we introduced, but I don't really see how it could be a real problem other than causing confusion when trying to debug.

What I find strange is that when you use a normal grid (as you wrote earlier) this doesn't happen. So it might be something related to the janus grid. But I'm not sure. We haven't had reports from other users about this, so it's not a common thing.

Frans Bouma | Lead developer LLBLGen Pro
mikeg22
User
Posts: 411
Joined: 30-Jun-2005
# Posted on: 16-Mar-2007 19:14:10   

I found a solution to this problem, no help from Microsoft tech support...

In the dispose of our forms, I include the code:


Dim systemAssembly As System.Reflection.Assembly = New System.ComponentModel.Component().GetType.Assembly
                Dim reflectTypeDescriptionProviderType As Type = systemAssembly.GetType("System.ComponentModel.ReflectTypeDescriptionProvider")

                Dim propertyCacheInfo As FieldInfo = reflectTypeDescriptionProviderType.GetField("_propertyCache", BindingFlags.Static Or BindingFlags.NonPublic)
                Dim propertyCache As Hashtable = DirectCast(propertyCacheInfo.GetValue(Nothing), Hashtable)
                propertyCache.Clear()

This clears out the TypeDescriptor cache that was holding our entity graphs hostage. Thought some other users may be experiencing the same issue...

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

1  /  2