Practical ASP.NET

Object-Oriented Programming for the ASP.NET Developer

Peter Vogel continues to look at the basics of O-O development to support the ASP.NET ObjectDataSource.

In my last column (Supporting the ObjectDataSource), I started looking at the code necessary to create objects that would work with the ObjectDataSource. In that column, I looked at creating an object factory class with methods that would convert rows in a Customer table into Customer objects, suitable for integration into an ASP.NET page using the ObjectDataSource. This column looks at the data class for those Customer objects.

A Simple Data Class
In my factory methods, I used this line of code to create my Customer object:

   cst = New Customer(rdr(0),  rdr(1),  rdr(2), …)

In that line of code, I passed each of the fields in the row that I had retrieved from the Customers table to my class' constructor. That constructor (and its enclosing class) would look like this (I've only included support for three columns of the table in this example):

Public Class Customer

Private _CustomerId As String
Private _CompanyName As String
Private _ContactName As String

Public Sub New()

End Sub

Public Sub New(ByVal CustomerID As String, 
   ByVal CompanyName As String, 
   ByVal ContactName As String)
  _CustomerId = CustomerID
  _CompanyName = CompanyName
  _ContactName = ContactName
End Sub

As you can see, all I'm doing in the class' constructor is capturing the values passed to it and storing them in private class-level variables (fields).

The rest of the Customer data class consists of properties to return those values stored in those fields. Here's a typical example:

Public Property CompanyName() As String
        Get
            Return _CompanyName
        End Get
        Set(ByVal value As String)
            _CompanyName = value
        End Set
End Property

If you want to support validating data in the middle tier, you'll put code to check the data entering your application in these properties. You might also declare some properties as readonly to prevent users from changing the value in the user interface (e.g. the property that returns the table's primary key). Alternatively, you could count on the validation in the page itself or in the database to keep users from changing any bad values.

If you're going to use the object from code rather than just tie it to the ObjectDataSource, there's some other enhancements you could consider. You might, for instance, add additional methods and properties to support setting multiple inter-related properties in one call. You could also add a property to track the status of the object (e.g.: Is this Customer to be deleted in the database?).

Typically, this class doesn't see as many changes as the Factory class does over time. A new property will occasionally be added to support changes in the database, validation code in some property will be enhanced, or a new method added to simplify working with the class.

Extending the Data Class
In the real world, it's an unusual application where the screen display matches the table design. For instance, if you're retrieving a SalesOrderLine, that table probably includes a column that holds the ProductId. However, in the user interface, you don't want to display just the ProductId: Your users want to see the ProductName.

This is part of the beauty of the Factory Method design pattern: you can extend your factory methods to incorporate whatever information you need in your data class. For instance, I might have a SalesOrderLineFactory with a method called GetSOLineBySOWithProdName. That method would look like this:

Shared Function GetSOLineBySOWithProdName( _
   ByVal SalesOrderNumber As String) As List(Of SalesOrderLine)
Dim rdr As SqlDataReader
Dim con As New SqlConnection(…connectionstring…)
Dim cmd As SqlCommand = con.CreateCommand()

md.CommandText = "Select * From SalesOrderLine s " & _
                                  "   Inner Join Products p " & _
                                  "     On s.ProductId = p.ProductId " & _
                                  " Where SalesOrderNumber = @SONumber"
cmd.Parameters.AddWithValue("SONumber", SalesOrderNumber)

Dim lst As New List(Of SalesOrderLine)
Dim sol As SalesOrderLine

con.Open()
rdr = cmd.ExecuteReader()
Do Until rdr.Read
    sol = New SalesOrderLine(rdr(0), rdr(1), rdr(2))
    lst.Add(sol)
Loop

Return lst

End Function

Extending the data class with related information is just one solution to the mismatch that typically exists between your business objects and your user interface. I'm going to look at a more sophisticated solution (involving another design pattern, I'm afraid) in a later column.

By the way, if you're starting to think, "Gee, all this code looks very much alike..." well, you're right. And coding these objects can get really boring really fast. It was repetitive code like this that drove me to create my first code generation solutions. In the meantime, of course, smarter people than me were creating object relational mapping (ORM) tools that would generate this code for a wider variety of scenarios than I was willing to handle.

However, my goal in these columns is more focused: To support the ObjectDataSource. In my next column I'll return to that mission showing how to extend the Factory objects to support updates.

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/.

comments powered by Disqus

Featured

Subscribe on YouTube