Typed DataSets are the easiest to demonstrate . We've already seen how to use these in previous chapters but for completeness let's quickly go over a demo. Let's use a version of the demo we've already created. To create a typed DataSet we use the Create DataSet wizard that is available when right-clicking the DataAdapter. When we create a typed DataSet using the wizard, the DataSet wizard gets its information from the DataAdapter. It then uses this information to generate a class based on the schema it retrieves from the database. Once the class is generated, the wizard then creates a DataSet that is an instance of the class. We will not go through this demonstration step by step because we've done this numerous times. Let's use our existing project, ADOBook07-01, and look at what we've done.
Describing the creation of a typed DataSet in detail is not easy. For this reason we will use some diagrams to explain the process. As we have said, a typed DataSet is based on a subclassed DataSet. In other words, the system creates a class inherited from the DataSet class. The new class has methods and properties that are customized based on the metadata provided by the DataAdapter. The new DataSet is then created as an instance of this new class. Once you see it laid out it is really easy to understand, and actually quite cool! Let's look at the block diagram in Figure 7.8.
Figure 7.8. Typed DataSet creation process.
Figure 7.8 assumes we are using the default DataSet names. You will want to use more descriptive names in your projects. We can see the class module generated by the wizard if we click the Show All Files button in the Project Explorer. The file is located under the DataSet1.xsd file node in the tree (see Figure 7.9).
Figure 7.9. Project Explorer with Show All Files selected.
You can open this file and look at it, but don't make any changes. Mostly what it does is make properties out of the various DataTable objects and their columns . We've already seen the benefits of how it makes our code more readable and easier to document. Besides this there is a slight performance gain as the compiler can use early binding to resolve addresses of members .
There are some design options to consider when using typed DataSets, and, admittedly ADO .NET can be confusing. There are three ways to approach application design using the DataSet:
Use untyped DataSets and do all your configuring in code.
Use all typed DataSets and use one DataSet per table.
Design your DataSets around your processes and use them as entities that define logical units of database access methods.
All of these methods can be misused or overused . Let's look at each and see when or when not to use each approach.
In general, untyped DataSets should only be used in situations where there is no way to know in advance what database access method will be required. A good example of this would be in an ad-hoc query generator where you have no way of knowing at design time what the user will want to access. Another good example is a report generator. This is similar to the query generator problem except that the output is sent to the printer and it probably has more formatting options.
Another place the untyped DataSet is very useful is when you are not using a database at all. It is entirely acceptable to use a DataSet to enter and store information in a disk file. You may wish to create an application in which a user enters data into a form. The data is them saved to a disk file and emailed to another location. At the other location the data is then read back in from the file and updated to a database. Even in this situation, however, it is more desirable to use a typed DataSet. We'll see how to make a typed DataSet from a user-defined DataTable later in this chapter.
Generally, I would not recommend using the untyped DataSet too often. There is a school of thought that disdains design-time anything. This camp wants to do everything in code at runtime. This usually makes for a system that is difficult if not impossible to debug and maintain. The opposite end of the thought spectrum is that anything that can be done using the tools provided is better than writing the code yourself. This tends to create code that conforms to a lowest common denominator syndrome, where the system is neither creative nor very useful.
I prefer a pragmatic middle-ground approach. Use the tools if they help speed things along, but don't become so reliant upon them that you forget how everything works.
The concept of one DataSet per table is a throwback to ADO and the RecordSet. This may be easier to conceptualize for the VB 6 programmer making the transition to .NET, but it is also very shortsighted. What you are saying is that you really don't want to learn the new technology; you want to stay with what is comfortable. Then why develop in .NET at all? Just stay with VB 6 and ADO. The whole point of the DataSet is to be able to create objects that do so much more than the old RecordSet. If you are going to use the technology, you should be willing to invest the time to learn it so you can put it to its best use. So that's my opinion on that approach.
There may be times where only one table is needed in a DataSet. If this is the case then by all means use one table. Don't feel that you must use multiple tables just because I said so.
So with that said, my recommended approach is to design your database access objects with your processes in mind. The one negative with this approach is that sometimes you end up with too many objects in your class structure and many of them can be redundant. That's all right when initially designing your system. It's not such a bad thing to design your classes and objects with complete encapsulation in mind. That is, each team member can feel free to create his own classes or objects to gain access to the needed data. Later on, after the code is unit- tested , it can be integrated into the main system and the classes can be refactored to promote code reuse and the other more esoteric goals of an object-oriented design. I am a strong proponent of regular refactoring to streamline the design and improve performance.
There is a philosophy of code development methodologies called Agile Methods or Extreme Programming. If you want to know more about these methods you can visit http://www.agilealliance.co/. I will not go into detail about these but they all contain a provision for regular refactoring of code. Essentially what this means is that you review your code and try to streamline it as much as possible. Just as in algebra, we can often simplify our code. When we simplify a math formula, we essentially make it easier to read and require less computation to arrive at the answer. Refactoring code has the same effect. Besides making it easier to read, we can often make the code easier for the computer to process. Needless to say, this will make the program execute faster and more efficiently .