informa-❑ Beginning Visual Basic 2005 Databases by Thearon Willis Indianapolis: Wrox, 2005❑ Visual Basic Database Programming by Rod Stephens Indianapolis: Que, 2002 ❑ Expert One-on-One
Trang 1Data Storage Design
The first step in building an application is to design it The better your design is, the fewer lems you will encounter later when you write the code To make the best design possible, youneed to look at the design from several points of view
prob-Chapter 4, “Object-Oriented Design,” looks at design from an object-oriented perspective It explainshow to select and refine the classes that will implement the application’s behavior Chapter 5, “User-Interface Design,” looks at design from the user’s perspective It explains how to lay out the applica-tion’s forms to make them as intuitive and useful as possible to the user
This chapter considers another aspect of design: how the data is stored In the real world, the largemajority of applications store data in relational databases, but that’s not the only option, and it’snot always the best option This chapter discusses different methods you can use to store differentkinds of data It talks briefly about relational databases, focusing on their strengths and weak-nesses so that you know when they are appropriate
It then discusses alternatives for storing data such as compiled-in data (data stored in constantsand other code elements), the System Registry, and various text file formats Even if you do need arelational database (and most applications do), there may be other pieces of information that arebetter stored by one of these other methods
Relational DatabasesMost large applications and many smaller ones use relational databases to store their data There’s
no room in this book to do justice to the topic, so I’m not going to try For more in-depth tion about relational databases, see a book specifically about them, such as one of the following:
Trang 2informa-❑ Beginning Visual Basic 2005 Databases by Thearon Willis (Indianapolis: Wrox, 2005)
❑ Visual Basic Database Programming by Rod Stephens (Indianapolis: Que, 2002)
❑ Expert One-on-One Visual Basic 2005 Database Programming by Roger Jennings (Indianapolis:
Wrox, 2005)
There isn’t room to cover relational databases in much depth here, but I do want to cover a few aspects
of relational databases so that you can compare them to the alternatives, and select the best data storagemethod for your application
A relational database stores data in tables You can think of a table as a grid where each column sents a specific piece of data (such as a name, address, phone number, or image) Each row in the tablerepresents a set of column values that are related For example, a row in the Customerstable wouldcontain data for a specific customer Figure 6-1 shows a simple Customerstable represented as a grid
repre-Figure 6-1: A row in a table contains values that are related
Rows are also called records, and columns are also called fields, so a record contains a collection of related
fields
A relational database may contain many tables, as shown in Figure 6-2
Storing data in this grid-like arrangement may sometimes be useful, but the real power of a relationaldatabase comes when you define relationships among the tables For example, suppose the Customers
table represents customers and identifies each customer with a CustomerId
CustomerId
2178172973287217619272981274
AnnBillCindyDanEvaFredGinaHarry
AceBlaughCheerfulDwelphEversnorCatabalpasParnathusPan
2451 Elm St
254 2nd AveOne Oak Ter
StateAZAZAZAZAZAZAZAZ
Zip8838288381883908838988376883828837688381
Trang 3Figure 6-2: A relational database contains multiple tables, each containing multiple rows of fields.
The Orderstable represents orders placed by the customers It identifies the customer who placed anorder with a CustomerIdfield It identifies each order with an OrderIdfield
The OrderDetailstable contains information about the items that make up an order It identifies theorder that contains an item with an OrderIdfield
Figure 6-3 shows how Customers, Orders, and OrderDetailstables are related graphically In this ure, customer 2176 placed an order represented in the Orderstable by a record with CustomerId 2176.That order has OrderId 1273 The order item details for that order are the records in the OrderDetails
fig-table with OrderId 1273
CustomerId2178172973287217619272981274
AnnBillCindydanEvaFredGinaHarry
AceBlaughCheerfulDwelphEversnorCatabalpasParnathusPan
2451 Elm St
254 2nd AveOne Oak Ter
StateAZAZAZAZAZAZAZAZ
Zip883828838188390883898837688382883688381
CustomerId21762121722878301817812121120
12731274127512761277127812791280
01/04/0601/04/0601/17/0601/26/0602/11/0602/17/0602/21/0602/22/06
01/17/0601/22/06
OrderID OrderDate
Orders
PaidDate
OrderId12731273127312741274127512751275
21571267368987281289289126782913
12312123
1214421361082
ItemID SequenceId
OrderDetails
Quantity
135
Trang 4Figure 6-3: A relational database uses field values to form relationships among records in tables.
Relational databases support the Structured Query Language (SQL), an English-like language that tells adatabase engine what records to retrieve from the database You can use SQL statements to join togethertables and select related records For example, the following query selects information about orders placed
by customer 2176 It returns them ordered by OrderIdand then SequenceId:
SELECT FirstName, LastName, Orders.OrderId, OrderDate, PaidDate, _
ItemId, QuantityFROM Customers, Orders, OrderDetails
WHERE Customers.CustomerId = 2176
AND Customers.CustomerId = Orders.CustomerId
AND Orders.OrderId = OrderDetails.OrderId
ORDER BY Orders.OrderId, SequenceId
This example searches for Customersrecords with the desired CustomerId, joins the resulting recordswith records in other tables, and sorts the result
Relational databases can define indexes on tables to make searching them for specific values faster Forexample, you could build an index for the Orderstable’s CustomerIdfield to make searching forrecords with a particular CustomerIdfaster
CustomerId
2178
172973287
AceBlaughCheerfulDwelphEversnorCatabalpasParnathusPan
2451 Elm St
254 2nd AveOne Oak Ter
StateAZAZAZAZAZAZAZAZ
Zip883828838188390883898837688382883688381
CustomerId21762121722878301817812121120
12731274127512761277127812791280
01/04/0601/04/0601/17/0601/26/0602/11/0602/17/0602/21/0602/22/06
01/17/0601/22/06
OrderID OrderDate
Orders
PaidDate
OrderId12731273127312741274127512751275
21571267368987281289289126782913
12312123
1214421361082
ItemID SequenceId
OrderDetails
Quantity
Trang 5Relational databases also provide for aggregating values: taking sums, averages, and so forth Takentogether, all of these features make relational databases very useful for searching, combining, sorting,and reporting on data Those are the strengths of relational databases.
Those features can be very useful in an application, particularly for typical business data describing suchentities as customers, employees, orders, inventory, and invoices They can even be useful for storinginformation about the classes that the program uses For example, the program might define a Customer
class and store information about it in the Customerstable The database itself won’t save and restore
Customerobjects, but it wouldn’t be hard to write code to move values from a Customerobject and
aCustomersrecord in the database and vice versa
Though relational databases are good at representing table-like data, they are not as good at representingcomplex data structures
For example, consider the small hierarchical data structure shown in Figure 6-4 Each node in the treecontains some information and has a collection of references to child nodes
Figure 6-4: Each node in this hierarchical data structure has a collection of references tochild nodes
One way to represent this data in a relational database is to build a Nodestable and a Childrentable.The Nodestable contains each node’s information and a NodeId The Childrentable connects NodeId
values to ChildIdvalues
Figure 6-5 shows the previous tree stored in a database with this structure Arrows show how to traceout the top two levels of the tree Start at the root node with NodeId 1 Next, find the records in the
ChildNodestable with NodeId 1and their ChildIdvalues give you the IDs of the children of node 1
To build the entire tree, you need to examine the ChildNodesrecords for each of the child nodes youfound in the previous step You continue in this manner, searching the ChildNodestable for the children
of the nodes you have found so far, until you find all of the nodes
To use a similar database design to store a network-like data structure, change the name of the ChildNodes
table to Neighborsand change the name of the ChildIdfield to NeighborId Now the Neighbors
table lists the nodes that are adjacent to a node in the network Restoring the network from the database
is a little more complicated than it is with a tree because you need to beware of loops For example, if thenetwork is undirected, if node A has node B as a neighbor then node B also has node A as a neighbor
137
Trang 6Figure 6-5: These tables store a hierarchical data structure.
Figure 6-6 shows a more compact database design In addition to information and a NodeId, each node’srecord now contains the index of its parent in the tree To find a node’s children, now you search for Nodes
records where the ParentIdrefers to the node whose children you are trying to find
Figure 6-6: This design stores a hierarchical data structure more efficiently
Although these database designs work, they are not very efficient because you need to perform a rate database search to find each node’s children If you have 1,000 nodes in the tree, you’ll need toperform 1,000 separate searches
sepa-InformationAnimalMammalSnakeCatBirdDogOwlReptile
NodeId12345678
ParentId01821251
Nodes
InformationAnimalMammalSnakeCatBirdDogOwlReptile
NodeId12345678
Nodes
NodeId1112285
ChildId2854637
ChildNodes
Trang 7I worked on one application that built and manipulated huge trees representing products stored in a relational database A typical tree could easily have 20,000 nodes, so loading a tree could take 20,000 or more database queries Even with the tables indexed, it took quite a while I left that project before they addressed this issue, although I did some tests that indicated that a serialized tree stored in XML would have reduced the load time from around five minutes to less than five seconds The section “XML” later
in this chapter says a bit more about XML.
Relational databases are great if you stick to their strengths: searching, joining tables, and ordering andaggregating results It’s also relatively straightforward to save and restore simple objects in a relationaldatabase
However, though you can coerce a relational database into storing more complicated data structures, it’soften quite inefficient You can make them store a tree or network, but you’re often better off considering
a different data structure
Note that you can also combine relational databases with other approaches For example, you can storeExtensible Markup Language (XML) text in a relational database and then use the XML data to represent
a hierarchical data structure Unless the application needed a lot of relatively small trees, however, therelational database wouldn’t add much to the solution
Relational Database ProductsAll of the major relational database products (such as SQL Server, Access, Oracle, Informix, and MySql)provide about the same level of functionality Some provide more features than others, but they all pro-vide basic searching, joining, and sorting Look at the product documentation to compare them and pickthe one best suited for your development team
The most obvious choices for many Visual Basic developers are the two Microsoft database products:Access and SQL Server Each has its benefits and drawbacks
Microsoft Access is extremely simple to use It’s also very easy to distribute an Access database with anapplication You simply copy the database onto the target computer and install the appropriate databaseengine Then the application can open the database and use it You don’t need to install (or pay for) theAccess product on the target computer In fact, you can use other tools to build the database initially, soyou don’t really even need the Access product installed on the developers’ machines
Although Access is the product used by many Visual Basic developers, it does have a few drawbacks.First, it does not provide the same level of support for multiple users that the more powerful databaseproducts do Second, the amount of data an Access database can hold is limited In particular, any singletable can hold no more than 1GB or 2GB of data, depending on the version of Access
In SQL Server, a table can contain as much data as will fit on the computer’s disks SQL Server also supportssome additional features that Access does not (such as database triggers, views, and transaction logging)
SQL Server is more complicated to manage, however It’s also a lot more expensive and it must beinstalled on the end users’ computers, so you need to buy additional licenses for them
Ideally, you would develop your program using Access, and then switch to SQL Server if you discoverthat the application has outgrown Access’s more limited capabilities Unfortunately the code is substan-tially different for working with Access or SQL Server
139
Trang 8Fortunately, Microsoft has recently provided an alternative growth path The Microsoft SQL Server 2005Express edition is a slightly restricted version of SQL Server that is available for free The main restric-tion is that a SQL Server Express database cannot have a total size greater than 4GB, while the size of afull SQL Server database is limited by the available disk space.
This means you can build your application with the free SQL Server Express product Later, if you cover that your database has grown too big, you can buy a more complete version of SQL Server Whenyou use SQL Server Express, you still need to install SQL Server Express on the end users’ computers,which is more difficult than installing the database drivers for use with Access, but at least it won’t costyou a lot of money
dis-Learn more about SQL Server Express at http://www.microsoft.com/sql/editions/express/default.mspx Compare the features of SQL Server Express with other versions of SQL Server at
http://www.microsoft.com/sql/prodinfo/features/compare-features.mspx
Compiled-In Data
The least-flexible way to include data in an application is to compile it into the code You simply typevalues into strings literals, assign values to constants, and so forth Because the data is embedded in thecode, you cannot make changes to it without recompiling
Though this isn’t a very flexible technique, it has a few advantages Loading data in compiled code isrelatively fast, and it’s fairly difficult for the user to mess with the data In contrast, it’s fairly easy for auser to read and modify data stored in an Access database
You can get slightly more flexibility by putting data in a dynamic link library (DLL) and linking the DLLinto the application You still cannot make changes to the data without recompiling, but at least youonly need to recompile the DLL and not the whole application If you don’t do anything to break com-patibility (changing function names, removing classes, and so forth), the main program can use the newversion of the DLL without recompiling
Normally, a Visual Basic executable does not require a specific version of an assembly containing a classlibrary You can change that behavior to require a specific version, but only if you strongly name the assem-bly Unfortunately, this requires a long sequence of arcane steps, so read the following paragraphs closely
Start by creating a new class library project In Visual Studio, select the File menu’s New Project
com-mand, select the Class Library template, enter the library’s name (for example, CompiledInDataLibrary),
and click OK
Add code to the class library For example, the following code defines a CompiledInDataclass with onepublic function named GetInstructions:
Public Class CompiledInData
Public Function GetInstructions() As StringReturn “Here are instructions for version 1.1”
End FunctionEnd Class
Trang 9Set the project’s version number In Project Explorer, double-click My Project On the Application tab,click the Assembly Information button Enter the assembly’s major, minor, build, and revision numbers,
as shown in Figure 6-7, and click OK
Figure 6-7: Enter the assembly’s major, minor, build,and revision numbers
Save the project so that you have a project directory to use if you haven’t already done so
Before you can sign the assembly, you need to generate a strong name key file To do that, you need touse the Strong Name tool, sn.exe To make using this tool easier, you can add its location to your path
In Windows XP, open the Control Panel and start the System applet On the Advanced tab, click theEnvironment Variables button, edit the Path system variable, and add the following path to the end(assuming sn.exeis in the default location):
C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin
Alternatively you can add the following statement to the end of your system’s AUTOEXEC.BATfile andreboot:
PATH “C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin”;%PATH%
Now, create the strong name key file Open a command window (select the Start menu’s Run command,
type cmd, and press Enter) and go to the assembly’s directory At the command prompt, enter a
com-mand similar to the following one You can give the strong name key file a different name if you like:
sn -k CompiledInDataLibrary.snk
141
Trang 10Back in Visual Studio, you need to enable assembly signing Double-click My Project again if you closed
it, go to the Signing tab, and check the “Sign the assembly” box Click the “Choose a strong name keyfile” drop-down, select <Browse >, select the key file you just created, and click Open The resultshould look like Figure 6-8
Figure 6-8: Select the strong name key file
Save the project and build the library
You have now built the strongly named DLL assembly and are ready to make an application to test it
Select the File menu’s New Project command, select the Windows Application template, enter the
appli-cation’s name (for example, UseCompiledInData), and click OK.
In Project Explorer, double-click My Project, click the References tab, click the Add drop-down, andselect Reference On the Add Reference dialog, click the Browse tab, select the DLL you just created,and click OK
Select the new reference Then, in the Properties Window, set the Specific Version property to True, asshown in Figure 6-9 This tells the executable to require the exact version that is referenced The Versioncolumn in Figure 6-9 shows that the library had version 1.0.0.1 in this example
Trang 11Figure 6-9: Set the class library reference’s Specific Version property to True.
Now you can finally use the compiled assembly Add a Label named lblMessageto the program’s formand add the following code to the form:
Public Class Form1Private Sub Form1_Load(ByVal sender As System.Object, _ByVal e As System.EventArgs) Handles MyBase.LoadDim compiled_in_data As New _
CompiledInDataLibrary.CompiledInDatalblMessage.Text = compiled_in_data.GetInstructions()End Sub
End Class
When the program starts, it creates a new CompiledInDataobject from the class library, and displaysthe result of its GetInformationfunction in the label
If you compile and run the program, you should see the text “Here are instructions for version 1.1.”
Here’s where the useful part begins Open the library project and change the Returnstatement in tion GetInstructionsto the following:
func-Return “Here are instructions for version 1.2”
143
Trang 12Rebuild the library and copy the DLL into the directory where the UseCompiledInDataproject built itsexecutable You should see the older copy of the DLL there already Just replace it.
Without rebuilding the UseCompiledInDataproject, run the executable in that directory You shouldsee the new message “Here are instructions for version 1.2.”
So far so good This is what you would expect if you hadn’t jumped through all the hoops to stronglyname the library You can update the data by replacing the old version of the DLL with a new version
Now, open the library project and change the Returnstatement to the following:
Return “Here are instructions for version 2.0”
In Project Explorer, double-click My Project; click the Application tab; click the Assembly Informationbutton; and change the major, minor, build, and revision values to 1.0.0.2
Again, rebuild the library and copy the new DLL into the directory where the UseCompiledInDataject’s executable is Now, when you try to run the executable, you’ll see the message shown in Figure 6-10
pro-Figure 6-10: A Visual Basic executable won’t run with a new version of astrongly named DLL
To make the application work with the new version of the library, open the executable project In ProjectExplorer, double-click My Project Select the References tab, click the library’s reference, and click theRemove button Then use the Add drop-down as before to add a reference to the new DLL Rebuildthe executable project, and the compiled executable will be ready to go
Be sure you make the proper kind of build If you copy the DLL into the executable project’s bin/Debug
directory, be sure you are making a Debug build.
By default, Visual Basic determines the kind of build automatically If you use the Build menu to pile the project, it assumes you are making a Release build If you compiled the project by running it
com-from the Debug menu, Visual Basic assumes you are making a Debug build.
If you don’t want Visual Basic to make this choice for you, use the Configuration Manager to select the configuration that you want to make When you install Visual Basic, the Configuration Manager is hid- den To make it available, select the Tools menu’s Options menu In the Projects and Solutions section, select the General page, check the “Show advanced build configurations” box, and click OK.
Now you can use the Configuration Manager command in the Build menu to display the dialog shown
in Figure 6-11 Use the drop-down in the upper left to select the Debug or Release configuration.
Trang 13Figure 6-11: The Configuration Manager lets you select Debug or Release builds.
Using these techniques, you can make changes to the compiled-in data and allow the executable to viewthe changes simply by copying the new DLL into the executable’s directory
Note that you can change the file version information (see Figure 6-7) without breaking compatibility.You can use the file version to keep track of different versions of the DLL without changing the assemblyversion information, which would force you to recompile the executable
If the changes you make will require an update to the executable, you can change the library’s versioninformation so the old executable won’t work with it
All of this is a lot of work to ensure that an old executable cannot run with a new library under some cumstances A different approach would be to give the library a subroutine called VerifyCompatibility.The executable program would call that routine, passing in some identifying value such as its version num-ber VerifyCompatibilitywould examine the value, decide whether the library can work for that ver-sion of the executable, and throw an error if there will be problems This wouldn’t be quite as foolproof asusing a strong named library, and it requires a little extra work to write and use VerifyCompatibility,but it would be a lot less confusing
cir-Resource F iles
Resource files contain values that the program can read at run-time One of the more interesting uses for
resource files is to allow forms to automatically display in more than one language To localize a form inthis way, open the form in the form designer Place controls on the form and set their properties as usual
Next, set the form’s Localizableproperty to True In the Properties Window, click the form’s Language
property, click the drop-down arrow on the right, and pick one of the languages on the list
145
Trang 14When you do this, Visual Studio creates a new resource file representing the language you selected Ifyou modify the form, the changes are stored in the language-specific resource file You will probablyneed to change the text in the form’s caption, labels, group boxes, and other controls on the form forthe new language You may also need to display different icons or pictures, or use different colors for thenew language All of these changes go into the resource file.
The resource files are normally hidden, but you can see them if you click the Show All Files button inSolution Explorer The resource files lie below the form’s branch The default file used when no betterlocalized file is available for the user’s system is named after the form and has an resxextension.Other resource files add a locale identifier before the extension
For example, suppose you build a form named Form1and then localize it for German Visual Studiobuilds a hidden default resource file named Form1.resxand a resource containing the German modifi-cations named Form1.de.resx
When the application starts, it automatically picks the best resource file based on the computer’s tings If the computer is using German, it picks the German resource file If the computer is not usingGerman, it picks the default resource file When it loads a form, the application automatically uses thevalues from the selected resource file
set-In addition to using the resource files automatically, your code can pull values from the files If youwant to use the same value for every language, you can add them to the project’s main resource file InSolution Explorer, double-click My Project and select the Resources tab Use the drop-downs to addresources to the file Now your code can retrieve values from the project resources
For example, suppose you add two string resources named FileNotFoundMessageand FileNotFoundCaptionto the program’s resource file Then you could use the following code to display themessage with the desired caption:
Example program UseResourcesincludes resource files named StringResources.resxand
StringResources.de.resx Each defines string resources named HelloMessageand HelloCaption.Double-click a resource file to open it in the resource editor Figure 6-12 shows Visual Studio editing the
StringResources.de.resxfile Notice the StringResources.resxand StringResources.de.resx
files at the bottom of Solution Explorer
Trang 15Figure 6-12: A resource file can contain strings, icons, and images.
The UseResourcesprogram uses the following code to display a message when you click the Click Mebutton (You can download this example at www.vb-helper.com/one_on_one.htm.)
‘ Display the value “Hello” stored in the resource files
Private Sub btnClickMe_Click(ByVal sender As System.Object, _ByVal e As System.EventArgs) Handles btnClickMe.ClickMessageBox.Show( _
My.Resources.StringResources.HelloMessage, _My.Resources.StringResources.HelloCaption)End Sub
Setting your system to use other languages would make testing localized applications cumbersome.Fortunately, you can make the program switch locales at run-time Add the following code to a form tomake it pick a new locale:
Imports System.ThreadingImports System.GlobalizationPublic Class Form1
Public Sub New()MyBase.New()
‘ Set the culture
147