Practical ASP.NET
Object-Oriented Updates with the ObjectDataSource
If you're going to use the ObjectDataSource in a real application, you'll need to support all of the CRUD activities. Peter Vogel extends his object model to do just that.
In previous columns (Supporting the ObjectDataSource and Object Oriented Development for the ASP.NET Developer) I walked through a simple model for developers wanting to move from single-tier development to multi-tier development. In an earlier column (In Defense of Single-Tier Applications), I also covered why you might need to make the move. To date, however, all I've covered is how to get data out of your tables and onto your page. To fully support the ObjectDataSource in any real world application, you'll need to support updates, deletes and inserts.
The ObjectDataSource assumes that you're going to implement the Factory Method pattern (as I described in "Supporting the ObjectDataSource"), which uses two classes for every business entity: a factory class that handles creating objects from database tables and a data class that carries the data to the page. The code to handle updates goes in methods in the factory object, because updates are just the reverse of the existing methods in the factory. Instead of converting rows into objects, these new methods convert objects into rows.
Implementing Updates
The ObjectDataSource expects you to have one method for each of the insert, delete and update operations. The ObjectDataSource will pass these methods an object that the factory produces, depending on what the user has done in your application's pages. For instance, if the user deletes a row in a GridView of Customers, then the ObjectDataSource will pass a Customer object representing that row to a method on your factory. It's your responsibility in that method to do whatever is necessary to delete the corresponding row in the database.
I selected the delete method because it's the easiest to implement. Here's a typical delete method that accepts the Customer object I used in previous columns:
Share Function DeleteCustomer(Cust As Customer)
Dim rdr As SqlDataReader
Dim con As New SqlConnection(...connection string...)
Dim cmd As SqlCommand = con.CreateCommand()
cmd.CommandText = "Delete from Customers " & _
" Where CustomerId = @CustId"
cmd.Parameters.AddWithValue("CustId", Cust.Id)
Dim intRecordsAffected As Integer
con.Open()
intRecordsAffected = cmd.ExecuteNonQuery
con.Close
Return intRecordsAffected
End Function
The update and insert methods are almost identical to this method, except for changes in the SQL statement and the addition of more parameters.
Handling Concurrency
However, you may want to consider concurrency issues (two users accessing the same record at the same time). For inserts, concurrency simply isn't a problem. Because the record doesn't exist until the user adds it, no other user can be working with a record that is about to be inserted.
My attitude is the same towards deletes: If one user deletes a record that another user is either trying to delete or update... well, life's just like that. But if that's too laissez-faire for you, then there are two scenarios you need to consider when deleting a record: (a) some other user is updating the record on another page but has not yet saved it, or (b) a user updated and saved a record on another page in the time between when the current user retrieved a record and has gotten around to deleting it.
First, I'm assuming that you're not going to lock records when retrieving them. That means there's nothing you can do about the first scenario: When the user doing updates finally saves the change, the record they've been updating will be gone. In your update code, the ExecuteNonQuery that executes the update will return 0 and the page developer can decide what to do with that information in the ObjectDataSource's RowUpdated event. (For more, see this column.)
I've discussed second scenario in other columns, also. My personal solution is to just delete the record based on the "He who updates last, updates best" rule. If you'd prefer to show the user the updated record and make them confirm the delete ,then see my previous column on handling optimistic concurrency in the ObjectDataSource. Fundamentally, the solution suggests that you build a Where clause that checks that no values have changed since the row was retrieved and skip deleting the record when any values have changed.
As I discuss in that column, for updates I do recommend one extension to the simple method I've discussed here. Only update those fields where the user has made a change (also discussed in my previous column). Most concurrency issues disappear if you avoid updating every field in a database row because the user changed one field.
In an earlier column, I pointed out that it's unlikely that your business objects will match the layout of your pages, and I provided a simple solution to that problem. In my next column, I'm going to return to that problem to provide a more sophisticated (and extensible) solution
.
About the Author
Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.