Two questions

Two questions

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


lukky posted on Tuesday, November 25, 2008

Hi,

First one is for Rocky: I have pre-ordered your upcoming book from Amazon, and you posted to your blog that an ebook version is now available, but through another store. Just so I don't go nuts and order it again as an ebook (I'm desperate to peek at it as I'm starting a new project and I want to use 3.6), is the book at the printer yet ?

Second, I've seen some discussion about concurrency handling, and there doesn't seem to be a clean solution sticking out as a "better" one. What I'd like to implement is a "first wins" scenario with warning to user when conflicts happen. The way I'm seeing it would be to only update modified fields. So I'm thinking of having two PropertyInfo objects for each property of my objects, one with the curent value, and one with the original value. Then, I'd generate the SQL dynamically to only include modified fields. Has anyone implemented such a beast ? Any forseeable pitfalls ?

Thank you.

rsbaker0 replied on Tuesday, November 25, 2008

lukky:
Second, I've seen some discussion about concurrency handling, and there doesn't seem to be a clean solution sticking out as a "better" one. What I'd like to implement is a "first wins" scenario with warning to user when conflicts happen. The way I'm seeing it would be to only update modified fields. So I'm thinking of having two PropertyInfo objects for each property of my objects, one with the curent value, and one with the original value. Then, I'd generate the SQL dynamically to only include modified fields. Has anyone implemented such a beast ? Any forseeable pitfalls ?

This is *exactly* the concurrency model that we use, but you don't really need two properties for each field.

Basically (and code generation will help with this), you need a common base class that maintains a dictionary of some sort of the original values. Then, you use a common property setter or helper (e.g. PropertyChanging(fieldname, oldvalue, newvalue)  that stores the original value the first time a property is changed.

When the object is updated, your CRUD layer can append the original values of the changed properties to the WHERE clause. If no records are updated, then a collision has occurred.

This model allows for lazy instantiation of the original value cache, so there is no overhead unless a field is changed, and then the overhead is limited to just the fields that are changed. I used a HybridDictionary which uses the more efficient list implementation when just a few entries are present and switches over to a hash mechanism automatically when necessary.

Remember to flush the original value cache when the object is successfully updated.

Note, with CSLA 3.5 and later, it's probably possible to avoid the separate dictionary and implement your own IFieldData and manage the old value directly in the FieldManager. However, much of that infrastructure in CLSA is marked as "internal", so this approach might require some CSLA changes.

SonOfPirate replied on Tuesday, November 25, 2008

I'm hoping you can explain your use case a bit more because I'm confused why this level of complexity is required - but I'm very interested in learning more.

In my experience, concurrency can be handled simply with a LastModified field that contains a date/time value.  When the record is retrieved from the data store, this value is read into your BO.  When an update occurs, this value is supplied and compared to the record's current value.  If different, then someone else has already changed the record and an error is thrown (as a SqlException which is handled in code).  If they match, then the update proceeds.  As part of the update, the value is also updated in the data store and sent back to the BO where it updates its value to once again match so subsequent updates will succeed.

I had one case where the owner wanted the application to prompt the user to overwrite the changes when detected.  To do this, I added a CommandObject that polled the record to see if a change has occurred by, again, comparing these dates - it was triggered by a HasChangedOnServer() method.  If the command returned a flag indicating that a change had occurred, I raised an event which was handled by the UI and prompted the user.  Depending on the user's selection, I would either proceed with the update passing in an additional parameter telling the db to ignore concurrency or I would terminate the operation.

As for adding the complexity of dynamic SQL into the mix.  The only benefit I've seen from this is to minimize the packet size sent from the application to the data store.  And, most times, it hasn't saved me enough to warrant the additional processing time and code complexity to implement.  I do know, however, that many ORM's on the market do enable this type of logic.  NHibernate is one, I believe.  It can be used with CSLA within the DataPortal_XYZ methods (instead of directly working with ADO.NET objects, use NHibernate to access and work with the data store).

All that said, I am very interested in learning more if you have a use case that requires working with the individual properties as you've described.  Please share more.

Thanks!

 

lukky replied on Tuesday, November 25, 2008

SonOfPirate,

I also do think it's a lot of work to dynamically generate the SQL. Let me try to explain with an example.

Let's say you have two different use cases that define two different BOs, but who eventually write to the same row of data. If I use the timestamp method, even though the two use cases don't impact on the same fields within the row, I'll still have a possibility for concurrency violations.

I guess I'm trying to put words on something for which I lack formal vocabulary, but in my mind, using timestamp for concurrency check doesn't do it at the proper "level". Timestamp will indeed check concurrency at the row level, but not at the BO level (if I may say so).

I hope I don't sound too confused.

Regards.

Curelom replied on Tuesday, November 25, 2008

You can use multiple timestamp fields for the different use cases.  If you have more than just a few use cases, you may need to look at redesigning your database.

JoeFallon1 replied on Tuesday, November 25, 2008

Luc,

1. The timestamp solution is the most straightforward to implement. It is also the method that Rocky uses in the Project Tracker sample application so you can see exactly how to do it. I recommend it. Many projects do not use concurrency checking at all so the timestamp method gives you the most basic level of concurrency checking and it is the easiest to implement.

2. I have to question how often it is important that 2 users be allowed to edit different fields of the same row of data and both be allowed to save their changes as long as they do not overlap. It sounds good in theory - but how often does it really happen? Building the required infrastructure to support this concurrency mdoel would be a cool and interesting project - of very little value to the business owner in most cases. If you already have the infrastructure then I guess it wouldn't hurt ot "just use it". But this is non-trivial to implement and in my mind you can spend your time doing other things and just implement #1 above.

YMMV.

Joe

Curelom replied on Tuesday, November 25, 2008

On concurrency,

We use a simple row version field.  The data is passed to a stored procedure that uses an update statement that includes row_version = row_version + 1 and the where clause has row_version = @row_version

The @row_version is the row version from the object.  If the rowcount returned is 0, then the stored proc raises a concurrency error.

Copyright (c) Marimer LLC