DataGridView bound to collection - Entity Deleted

Posts   
 
    
Posts: 18
Joined: 26-Jul-2006
# Posted on: 26-Jul-2006 01:38:45   

I am an LLBLGen newbie... only a few days under my belt... so please pardon my ignorance.

I am trying to follow the Rapid C# Windows Development book sample, and I ran into a problem deleting the entity. After deleting the entity, the DataGridView tries to re-paint, and I get an exception for ORMEntityIsDeletedException.

I hacked the code so that after the delete is performed, and before the edit form is closed, I update the datasource for the DataGridView.

If you delete an entity, how should you typically handle any lingering collections that reference that entity?

I followed the book as closely as possible, but I'm using LLBLGen Pro 2.0, so there were a few differences.

Thanks!

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 26-Jul-2006 07:33:41   

Well you scenario is not clear to me. Are you deleting an entity from a collection that is bound to a DataGridView? or Are you deleting an entity which has a related collection bound to a DataGridView?

Also how do you delete the entity? And finally, would you please post the stack trace of the generated exception?

Thanks.

(edit) Would you please state the runtimeLibrary version you are using?

Posts: 18
Joined: 26-Jul-2006
# Posted on: 27-Jul-2006 01:03:42   

Sorry for not providing more information before. I was thinking maybe someone else tried to go through the Rapid C# development book using LLBLGen 2.0 and had seen the same thing.

Here are some more details.

  1. Set up an project against the AdventureWorks database
  2. Use the Self-Servicing, 2 Class template
  3. Build an Order Search form, with a DataGridView control to list the orders
  4. The DataGridView is data-bound to Orders, defined as:

SalesOrderHeaderCollection Orders = new SalesOrderHeaderCollection();
Orders.GetMulti(Filter, 100, Sort, Relations);

  1. Set the cell content double-click event to do the following:

SalesOrderHeaderEntity Order = dgvResults.Rows[e.RowIndex].DataBoundItem as SalesOrderHeaderEntity;
frmOrderEdit OrderEdit = new frmOrderEdit(this, Order);
((frmMain)this.MdiParent).LaunchChildForm(OrderEdit);

  1. In the frmOrderEdit form, keep a reference of the Order passed in with a member variable _order.

  2. When the user clicks the delete button on the form:


if (MessageBox.Show("Are you sure you want to delete this order?", "Confirm Delete", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
{
    _order.Delete();
    this.Close();
}

  1. When YES is clicked, _order.Delete() is successfully called. When this.close() is called, an exception occurs while the DataGridView in the other form tries to re-paint. Here's the call stack:

at System.RuntimeMethodHandle._InvokeMethodFast(Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner) at System.RuntimeMethodHandle.InvokeMethodFast(Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at System.Reflection.RuntimePropertyInfo.GetValue(Object obj, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture) at System.Reflection.RuntimePropertyInfo.GetValue(Object obj, Object[] index) at SD.LLBLGen.Pro.ORMSupportClasses.EntityPropertyDescriptor.GetValue(Object component) at System.Windows.Forms.DataGridView.DataGridViewDataConnection.GetValue(Int32 boundColumnIndex, Int32 columnIndex, Int32 rowIndex)

I am using WinForms C# 2.0 with LLBLGen Pro 2.0.0.0 Demo.

I chatted with a colleague about this, and I understand why it's happening. I am deleting an entity that is contained within a collection that is bound to a DataGridView. Rather than passing a reference to the entity into the edit form, I could pass its ID and get a new instance of the entity. But since the Rapid C# Development book was passing a reference to the entity and deleting it, without getting this error, I thought I'd see if this is something that I should be able to do.

Thanks!

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 27-Jul-2006 07:57:27   

I guess you could still pass a reference, but then you would have to remove that entity from the collection bound to the grid and re-bind.

But I'd prefer that you pass the Id to the edit form and use it to delete the entity from the database. But for a better user experience you then have to update your grid to reflect the changes undertaken in the edit form.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 27-Jul-2006 10:43:05   

This looks indeed like the question Charles posted on the helpdesk system (q1109) is that correct?

Frans Bouma | Lead developer LLBLGen Pro
Posts: 18
Joined: 26-Jul-2006
# Posted on: 27-Jul-2006 15:55:10   

Otis wrote:

This looks indeed like the question Charles posted on the helpdesk system (q1109) is that correct?

I'm not sure if you're asking me... but if so, I don't know -- I can't see that ticket in helpdesk.

Walaa wrote:

But I'd prefer that you pass the Id to the edit form and use it to delete the entity from the database. But for a better user experience you then have to update your grid to reflect the changes undertaken in the edit form.

Of course, the next natural question: What would you recommend for informing other active forms in the application that an item has been deleted from the database (or really that an item has been updated, because an edit may justify a refresh too)?

I could foresee something event driven, where an application event could be raised when an order is updated/deleted, and forms with order data could subscribe to those events, and refresh their data as needed. Maybe the forms don't automatically refresh their data all the time, but rather when they become active, they could check to see if an event has been captured and prompt the user for whether or not to refresh the list. In the case of a List/Edit/Delete scenario, it would make sense to just automatically update the list when the Edit/Delete form is closed, if the event has been raised.

I plan to look into the CAB from Microsoft. I wonder if that block handles some of this stuff.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 27-Jul-2006 17:25:14   

spider_from_ohio wrote:

Otis wrote:

This looks indeed like the question Charles posted on the helpdesk system (q1109) is that correct?

I'm not sure if you're asking me... but if so, I don't know -- I can't see that ticket in helpdesk.

The construct described in that question is very similar to what you're using: ask for a delete, if confirmed, proceed which then makes the grid go bezerk.

If you're not working with Charles, it's not the same project, but it's likely the same cause. The Delete() action makes the collection to raise an event that it's changed, the grid then re-reads the contents, but because the Delete() marks the entity as 'Deleted' the entity will throw an exception.

Frans Bouma | Lead developer LLBLGen Pro
Posts: 18
Joined: 26-Jul-2006
# Posted on: 27-Jul-2006 17:41:34   

So if an entity is deleted, and it was part of a collection, is the collection informed in any way that an event can be raised? I saw the ListChanged event, but that seemed to be for when the collection itself is changed, versus an entity in the collection being deleted.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 27-Jul-2006 19:13:34   

spider_from_ohio wrote:

So if an entity is deleted, and it was part of a collection, is the collection informed in any way that an event can be raised?

yes. It will raise a ListChanged event, which will be echo'ed by the EntityView object which is bound to the grid and which represents the collection to the grid. (a collection is bound to the grid via an EntityView)

I saw the ListChanged event, but that seemed to be for when the collection itself is changed, versus an entity in the collection being deleted.

The collection raises 2 events in this case as well: EntityRemoving and EntityRemoved. In EntityRemoving, you can cancel the delete by setting teh Cancel property of the event args to true. The remove is then canceled.

Frans Bouma | Lead developer LLBLGen Pro
C4 avatar
C4
User
Posts: 32
Joined: 12-Nov-2005
# Posted on: 28-Jul-2006 01:21:13   

Otis wrote:

This looks indeed like the question Charles posted on the helpdesk system (q1109) is that correct?

Same scenerio, different projects / people. It seems we both have read the "Rapid C# Windows Development - CV 2005, SQL & LLBLGenPro" We both seem to be running the 060722 runtime libraries and thu both have the same problem when removing an entitiy from a collection tied to a datagridview.

I had no luck subscribing to the EntityRemoving / EntityRemoved events.. as they never fired for me (I think becuase in the child form I had access only to the Entity, not the EntityCollection, and calling Entity.Delete() did NOT trigger those events)

What I did was create a sender object on the child form, create the child form setting the sender object to this on the parent form, and using the sender object to set the datagridview.datasource to null, deleted the entity, and uses a BubbleEvent on the clild form to inform the parent form to reload the grid thus setting the datagrid.datasource back again.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 28-Jul-2006 09:44:58   

C4 wrote:

Otis wrote:

This looks indeed like the question Charles posted on the helpdesk system (q1109) is that correct?

Same scenerio, different projects / people. It seems we both have read the "Rapid C# Windows Development - CV 2005, SQL & LLBLGenPro" We both seem to be running the 060722 runtime libraries and thu both have the same problem when removing an entitiy from a collection tied to a datagridview.

Ok simple_smile

I had no luck subscribing to the EntityRemoving / EntityRemoved events.. as they never fired for me (I think becuase in the child form I had access only to the Entity, not the EntityCollection, and calling Entity.Delete() did NOT trigger those events)

No the events are raised when you remove the entity from the collection. simple_smile This is done when the user for example selects a row in the grid and presses DEL, or when you manually call Remove() on the collection. Sorry for this confusion.

Frans Bouma | Lead developer LLBLGen Pro
Posts: 18
Joined: 26-Jul-2006
# Posted on: 02-Aug-2006 16:30:02   

I actually found the same as Charles and ended up implementing the same solution as a quick-fix.

But, for a real app, I may have multiple windows going that each have some sort of an order list. I visualize a screen where 1 window may be showing "My Orders" and another window lists "Orders Shipping Today" and another for "New Orders" or something like that... In that case, there would need to be some event for "OrderUpdated" or something, and each of those windows would subscribe to the event, refreshing their own lists.

I haven't gotten into the CAB yet or the Smart Client Factory deal, but I plan to... and I expect the CAB to give some guidance on something like this, but we'll see.

Thanks guys, I'm glad it wasn't just me wink

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 02-Aug-2006 20:32:41   

You should subscribe to the correct events exposed by the entity collection and act accordingly. If two or more screens all bind to the same object, that's fine, but consider that with multiple screens you also have multiple currencymanagers for the binding, which can lead to difficulties if you rely on the fact that everything has to be kept in sync automatically.

Frans Bouma | Lead developer LLBLGen Pro