How to handle "Row not found" exception when other user deletes child records ?

How to handle "Row not found" exception when other user deletes child records ?

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


lukky posted on Friday, January 23, 2009

Hi,

We use CSLA 3.6 + Linq to SQL (generated by the CSLAContrib CodeSmith templates).

The situation is that multiple users can edit the same parent + child at the same time. Though they can't delete a parent (they can only mark it as inactive), they are allowed to delete child objects.

The scenario is as follows:

As expected, I get a "Row not found" from the Linq to SQL DataContext.

When we started this application, it was agreed that the last person to Save() would have priority (last wins scenario). This means that in the situation above, user A's call to Save() should restore the deleted Child.

Since this Child object was originally loaded from the Database, then the call to Save() in A will route it to Child_Update(), and in there the exception gets thrown.

What would be the preferred way to "force" a call to Child_Insert() to recreate the deleted object ? Can we simply call Child_Insert() from within Child_Update() ? Are there pitfalls to watch for ?

Thank you

rsbaker0 replied on Friday, January 23, 2009

lukky:

...

When we started this application, it was agreed that the last person to Save() would have priority (last wins scenario). This means that in the situation above, user A's call to Save() should restore the deleted Child.

...

This doesn't answer your question, but I disagree (strongly) with this concurrency model.

Any type of concurrency collision is going to cause someone to potentially lose the work they have done.  One solution completely hides the data loss while the other makes it visible.

With "last one wins", the person whose changes are lost isn't even aware that it is happened: 

"Gee, I know I updated that invoice yesterday. I don't know where my changes went!"

At least with "first one wins" you can tell the "loser" that a collision occurred and give them the opportunity to refresh their screen and rekey the data.

Some people don't like either one and implement locking mechanisms (e.g. you prevent two people from working on the same invoice) to avoid the situation entirely, but this is not without it's own pitfalls and complications.
 

 

lukky replied on Friday, January 23, 2009

Hi,

I do understand your position, but as I said, this was discussed with the customer's representatives, and it was accepted like that.

One thing to know is that we're working with a very small group of users, and normal operations would have it that each inside sales rep will only deal with his own customers, so clashes like this one would be very rare.

First one wins could work, and you say, we could warn user about it, but we'd have to go back and discuss the issue again with the client, and we want to avoid that. We have settled on a model, and we'll try to make the best of it.

One of the reason we want to avoid going into a mega-concurrency-scheme is the fact that this is our first ever project with CSLA.NET, and also the first with Linq-to-SQL, so we're kind of learning as we go, but it's OK, because all those free hours that we put in, we see them as an investment into our knowledge.

One of the problem is our lack of proper understanding of how things should be done with L2S. I would have personally been very happy to use ADO.NET with CRUD operations right into the BO, but there are no currently available templates to generate such BOs, and we didn't want to spend precious time just coding them manually. I prefer spending more time learning new stuff than simply pounding code at my keyboard.

L2S represents quite a change in philosophy, and we understand from the fact that Rocky endorses its use in his book, that we might want to use it.

Some people would say that we're doing a crappy job, and they may be right, but then it can't be worse than the previous group of coders that this customer used to develop his business application. And I'll say that a contract is a contract, and I must put bread and butter on my family's kitchen table.

But I digress...

If there's anyone in this forum with enough hands on knowledge that could lend us a hand in understanding the proper way of doing things, then we'd be happy to listen. The problem is that there's an impedance mismatch between the written material to which we have access, and the templates that we have available to do the job (CSLAContrib CodeSmith templates) , so we have very little to base our understanding on.

Best regards.

rsbaker0 replied on Saturday, January 24, 2009

Technically, I'm not even sure I would consider the original case you mentioned as a collision unless user #2 actually changed the specific child that user #1 deleted. Typically, only dirty/changed objects are saved, and when user #2 saves his root BO that contains a copy of the now deleted child, normally the child would  be ignored during the save operation by user #2 unless he had also modified it.

That being, you can do anything you want during your DataPortal_Update implementation, and if you wanted to loop through the child collection to be sure that each child still existed in the database, and rewrite back out if it had somehow vanished, I think it's possible.

Still, it seems like there is much potential for peril in the general case. The deleted child may have had foreign key references to it (normally "grandchild" type objects but not necessarily). These are now gone also and you might not be able to put them back. Also, if the child object uses autonumber keys (e.g. IDENTITY in SQL Server), you can't always easily write them back out with the same key, etc.

Speaking of digression, I completely agree on the CRUD comments. I didn't want to write that code either, so we have bolted CSLA over an ORM which we use a small subset of to generate the CRUD code. (I started with the CSLA.NHibernate sample and converted it to use the now seemingly abandoned Wilson ORMapper).  We implement concurrency simply by including the original values of anything that has changed in the WHERE clause of the UPDATE statement that is generated, so that if the record isn't in the database anymore, nothing is updated and we detect this as a concurrency failure and just throw an exception. There isn't even currently any specially handling for this in the UI -- it's an error just like anything else.

I agree that the documentation and sample project seem to only cover a set of use cases that are on the basic side, but I've found CSLA to be enormously flexible and have managed to abuse it in ways that I'm sure would be frowned upon but seem to work very well. :)  There isn't necessarily just one way to do things.

lukky replied on Saturday, January 24, 2009

Yes,

I think I'm starting to see the enormous power that CSLA's flexibility puts in our hands, and as with anything else, it can be intimidating at first.

You are right about the problem only occurring when #2 modifies a deleted child. And to be quite honest, the only reason we have caught it is because we pounded at the application with the objective of crashing it. I don't think it will ever happen in real life, and I don't want to spend a whole lot of time fixing it. I'll simply pop a message to the 2nd user telling him that a child was deleted and it can't be restored.

The shift to using BOs is not as easy as I would have thought, coming from ADO.NET DataSet, TableAdapter and so on, where you have full designer support and almost no CRUD to worry about. I guess this is what I would like to see in the future for CSLA, with regards to designer tools.

Anyway, thanks for sharing your toughts on the subject. I've been trying to read every blog I find on those subjects, ie L2S + concurrency handling + DataContext lifetime etc., and I need to let all this sink in slowly.

Regards.

JoeFallon1 replied on Monday, January 26, 2009

As stated you have full control over your DataPortal_XYZ code.

The use of L2S for Fetching records makes a lot of sense.

But I think Rocky used SPs for the Insert/Update/Delete operations to avoid issues like this with the DataContext.

I guess what I am saying is you do not have to be "married" to L2S for *all* of your DP operations.

Joe

 

lukky replied on Tuesday, January 27, 2009

Joe,

Thanks for the "wake up" call. Sometimes we are so deep into the "funnel" that we fail to see what's around.

Your comment about not being "married" to L2S for *all* operations is very appropriate.

I think one of my biggest personal hurdle is that I tend to insist on things being done always the same way. I tend to lose it if I do things different ways in similar situations, but that's probably something I'll have to develop: learn when to generalize, and when to specialize.

Thanks for the heads up Smile [:)]

Copyright (c) Marimer LLC