Getting the Horse List

[Previous] [Next]

The first thing we wanted to implement completely was the procedure for getting a name pattern filtered list of horse names.

Moving the Test Stub Code

So we started by moving the test stub code away from the HorseManager class and into the HorseFetcher class. It was a good thing we did that because it helped us realize that we had chosen too narrow a name for the method in the fetcher class.

The name we first gave this method was simply GetList, and it accepted as its only parameter a name pattern string. We now realized that such limited filtering wasn't going to be good enough. We had to make it possible to filter horse lists in other ways too. We felt we had two options:

  • We could change the name to reflect the fact that this method would accept a name pattern filter only. As other needs were uncovered, we could design other methods to handle other types of filters.
  • We could change the parameter in such a way that the method would be able to accept other filtering alternatives as well as name patterns.

Realizing that this wasn't a critical decision, we went for the first of these options.

Why wasn't it a critical decision? Well, because a data fetcher normally has one client only. The only kind of object likely to access a HorseFetcher object is a HorseManager object, at least in the kind of architecture we're talking about now. In this respect, data access objects differ from main business objects. When a developer installs a main business object, making it available to other developers, he or she must assume that the object over time might have many different clients belonging to many different client applications. When that same developer, perhaps a year or more later, needs to modify the class, he or she won't even know about some of these clients. Main business objects are meant to be reused that way. That's why we recommend that clients always access main business objects through separate COM interfaces.

This loose relationship between a main business class and most of its clients makes it nearly impossible to change one of these clients and then change the main business class to suit that particular client. If you try, you're almost guaranteed to destroy the relationship between that main business class and other clients.

The relationship between a main business class and its data access classes is different. They belong to each other. In most cases, you can freely change them to suit each other, just as long as your changes don't affect any of the clients of the main business class.

Given this close relationship between main business and data access classes, we can now state a convenient goal regarding our data access classes. We can concentrate on the task of making sure everything works for the requirements we know about and postpone all discussions of future requirements.

In fact, we could have left things as they were, without changing the name of the GetList method, but we wanted to make it perfectly clear what this method does. So we changed the name from GetList to GetListForNamePattern, which is evident in the following code:

Public Function GetListForNamePattern _  (ByVal strNamePattern As String) As Recordset Dim rs As ADODB.Recordset Set rs = CreateObject("ADOR.Recordset") rs.Fields.Append "HorseId", adInteger, 4 rs.Fields.Append "HorseName", adVarChar, 20 rs.Open rs.AddNew rs!HorseId = 10 rs!HorseName = "Nijinsky II" rs.AddNew rs!HorseId = 20 rs!HorseName = "Nearco" Set GetListForNamePattern = rs End Function

Calling the GetListForNamePattern Method

The next thing we needed to do was make the main business object—the HorseManager—call the GetListForNamePattern method in a proper way. This was easy, as the following piece of code proves:

Private Function GetListAsRSForNamePattern _ (ByVal strNamePattern As String) As Recordset Dim objFetcher As RaceDataAccess.HorseFetcher Set objFetcher = CreateObject("RaceDataAccess.HorseFetcher")  Set GetListAsRSForNamePattern = _ objFetcher.GetListForNamePattern(strNamePattern) End Function

Testing the Result—Still with Test Stub Code

With one irritating exception, the code worked fine the first time we tested it. We had moved the test stub code, step by step, from the facade through the main business object and now into the data access class. We had also in successive steps added delegation to the classes involved. Our design and code still worked. The only unfinished step was the final one, and we were ready to take it. What didn't work? We'll tell you because it happens every time we generate code from a Rose model to a new ActiveX DLL project in Microsoft Visual Basic.

Every Visual Basic project has a property named Startup Object as shown in Figure 13-1. When Rose creates a new Visual Basic project, Rose sets the Startup Object of the project to the special Sub Main procedure. In most ActiveX DLLs there's no need for a Sub Main procedure, so we would have liked it much better if Rose could have generated None as the startup object, as in Figure 13-1, or at least given us a chance to set that as an option before code generation. As things are now, we have to modify the property manually in every new project. To be truthful, it's not so much having to set it manually that bothers us. It's that we always forget to do it, making Visual Basic give us nasty messages, as in Figure 13-2.

Anyway, we took up the problem with the Rational guys responsible for the integration of Rational Rose and Visual Basic. They told us they couldn't help it. They would have loved to set the property for us at code generation time, but the Visual Basic API didn't support it.

So we now turn to Microsoft. "Hey, guys! Add support for this in the Visual Basic API, please! That would help not only Rational Rose and Microsoft Visual Modeler but, chiefly, the increasing population of developers who use Rose or Visual Modeler to model their applications!"

click to view at full size.

Figure 13-1. For every new Visual Basic project generated by Rose, we must manually set Startup Object to None. (Yes, we know that threading and unattended execution must be handled as well. We'll come back to those issues in Chapter 14, "Using Microsoft Transaction Server," when we explain adaptation to MTS.)

Figure 13-2. This is what we don't want!

Replacing the Test Stub with Real Database Access Code

After checking that we still got the same set of horse names displayed in our test form, even though we had moved the test stub code again, we replaced the test stub code in the HorseFetcher class with real database access code. Our ultimate goal is to have efficient database access code, but we'll start simply. We'll describe enhancements of data access procedures in Chapter 17, "Securing Good Database Performance and Scalability," but here's the code we wrote for the first version of the HorseFetcher class:

Public Function GetListForNamePattern _ (ByVal strNamePattern As String) As Recordset Dim rs As ADODB.Recordset, strSQL As String Const strConn = _ "Provider=SQLOLEDB.1;Server=RACING_SERVER; " & _ "User ID=sa;Initial Catalog=MSPress_ADBRacing" strSQL = _ "SELECT HorseId, HorseName " & vbCrLf & _ "FROM Horses " & vbCrLf & _ "WHERE HorseName LIKE '" & _ "strNamePattern & "' " & vbCrLf & _ "ORDER BY HorseName" Set rs = CreateObject("ADOR.Recordset") rs.ActiveConnection = strConn rs.Source = strSQL rs.LockType = adLockReadOnly rs.CursorLocation = adUseClient rs.CursorType = adOpenStatic rs.Open Set rs.ActiveConnection = Nothing Set GetListForNamePattern = rs End Function

There are several important points to make about the preceding code:

  • Here we hard-code the connection string. In a real production environment, this approach isn't very good. We're aware of it, but for the time being, this is what you get. We'll talk briefly about other ways to set up a connection string in Chapter 23, "Some Final Issues."
  • We use one ADO object only—an ADO recordset. This also works, but for a real environment it might not be what you want. In Chapter 17, we'll come back to this issue, describing the use of ADO Command objects.
  • The recordset the GetListForNamePattern method creates is read only. This is good or even ideal because the method supplies a list of names to be read and displayed only. The list also contains the corresponding IDs to be used for further actions on the names. There's no need to waste resources making such a recordset anything other than read only.
  • We use client-side cursors, and we disconnect the recordset by setting its ActiveConnection property to Nothing. To be able to disconnect it, we must make it use client-side cursors. To be able to marshal it to other processes and machines other than the one on which it was created, we must disconnect it. So we have no choice: we need to make it use client-side cursors and we need to disconnect it.
  • This point is extremely important. If you want to create a scalable application based on ADO recordsets for data transport, you must disconnect any recordset from the database as soon as it's created, and you must be able to marshal it between processes and machines.

  • We make the recordset use a static cursor. This means that ADO will take a snapshot of the appropriate part of the database and store the entire snapshot in the recordset. And a snapshot is exactly what we need to be able to disconnect the recordset from the database; you can't disconnect a recordset until the entire result set is moved from the database to the recordset.

Testing Real Database Access Code

Now things are interesting. We have real database access code, and we're eager to test it. And lo and behold! It worked the first time again, as you can see in Figure 13-3.

Figure 13-3. Now real data from the database, in sync with the name pattern, populates the list box.

This, however, is no surprise. Of course it should work. We've been very careful at every step, and we've made sure that we have good code all the way. The test stub code told us quite clearly what our job was, and since we're no amateurs we did our job and expected it to work. You're a pro yourself, so you too should be able to have this kind of code work the first time you test it.



Designing for scalability with Microsoft Windows DNA
Designing for Scalability with Microsoft Windows DNA (DV-MPS Designing)
ISBN: 0735609683
EAN: 2147483647
Year: 2000
Pages: 133

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net