One data modeling technique that's especially popular with larger data models is to name all primary keys the same. For example, suppose that every table in a database has a primary key column called ID. Another common field is a timestamp, often used for optimistic concurrency or merge replication.
As the data-layer developer assigned to the project, you need to build a strongly-typed dataset on top of that data model. Creating it in Visual Studio is easyyou just drag and drop tables from the Server Explorer onto the DataSet designer. But what happens if your DBA decides to add a new field to every table? Or rename the primary key? Or get rid of the timestamp field? A DBA can hack up an SQL script in no time to change all the tables at once. But it's a little harder for a Visual Studio developer. The naïve approach would be to manually edit every table. But that's boring, repetitive work, the kind of work likely to introduce errors and inconsistencies into your application. Moreover, in a prototyping environment, the data model is likely to changea lot. And manually editing every table is not the kind of work you want to do every day.
What you really need is a reusable type that will abstract the information common to every table. That way, if your DBA makes any kind of global change, such as renaming the primary key field, you'll have to change it in only a single place.
To create this reusable type, add a new complexType
tag directly underneath the schema tag of your XSD document. This complexType
looks just like the rest of the table definitions that were created automatically, except that by default, Visual Studio creates all types as anonymous. That means it doesn't name its types because they're all defined in-line. Because you want to use the same type more than once, you can simply add a name attribute, like this:
<xs:element name="ID" type="xs:string" />
To inherit from this type, just use the extension tag, like this:
<xs:element name="Floor" type="xs:unsignedByte" />
<xs:element name="RoomNumber" type="xs:string" />
Think of the new <baseTable>
element as a base class. The element functions as an abstraction representing those columns common to every table. When fields common to multiple tables change, you need only change the base class. When you switch back to the visual designer, you'll notice the new type.
|Figure 2. Back to the Visual Studio Designer: Notice that the designer now shows the new complexType, which abstracts those fields common to all tables.|
It's a little surprising how harmless it is to make such alterations to the schema. Microsoft has done a great job of making sure that you can modify the schema either in the Visual Designer or in the source code side-by-side without either interfering with the other. Unlike developing Windows Forms, there's nothing dangerous about alternating between the designer and the underlying schema. The formatting may change a little, or your white space may get eaten up, but those are the only
things that will change.
Now comes the big payoff. If your DBA removes the timestamp
field, or renames the primary key, or adds a new field to every table in the database, you don't have to change every table manually. Just make your changes in the tableBase
type and all of its subtypes will inherit the changes. Even better, you can extend the concept of inheritance to create a hierarchy with an arbitrary number of levels. Listing 2
shows a sample schema that uses inheritance to create a hierarchy three levels deep. Any change in a type will automatically appear in all of its subtypes.
It's important to notice that none of this changes the behavior of the code generated by Visual Studio. Not at all. As the designer of the dataset, you're far better prepared for a changing data model. You can now define a type in one place and extend it throughout your XML schema, just like a class in C#. But the abstraction remains invisible to client developers. They don't know how much work you saved because to them, it appears as if all the alterations were manual.
Of course, there's a downside as well. You've introduced some reusable types into the schema definition, but that reuse doesn't translate into an OO-style inheritance tree in the typed DataSet. The type tableBase
remains an artifact of the schema, one that a client developer will see only through its subtypes. In other words, this technique gives you code re-use, but not polymorphism.