/strongerTDS

comment(s) 

Stronger Typed DataSets from XSD using CodeDom

http://www.brains-N-brawn.com/StrongerTDS 4/21/2002 casey chesnut

Introduction

Err, umm ... so i use Typed DataSets ALOT, and still think they are the greatest thing since <insert great thing/>. This includes all flavors of DataSets, from weakest to strongest:

But if you notice my domain, the 'brawn' part means i take the adjective 'strong' seriously. So i decided to put Strongly Typed DataSets on a strict workout regimen to beef them up and make them stronger

XSD

XML Schemas are having a major impact in the computing world. At a high level, as industries agree upon standard schemas when they start exposing their web services externally, you can code against that one schema and be able to consume the web services of anybody that conforms to that schema. e.g. i have integrated the google web service into my /pass search engine framework. Had written my own web crawler, which had found about 400 sites for the particular regular expression i fed it. By feeding it the output from the google web services, it was able to find about 100 more on a separate data island. If another search engine were to expose a similar search web service, they should follow the same schema, and then my code could call them or google just by simply changing the url and authentication credentials. Throw in UDDI, and it becomes insanely cool [OT: i hope google ends up exposing their groups over the web too. i wrote a similar NNTP web service about a year ago that can be found here: http://www.brains-N-brawn.com/newsgroup NOTE: i did not know about Typed DataSets at that time]

At a lower level, an XSD file is used to generate strongly typed DataSets. The XSD file can be generated numerous ways:

NOTE: i typically create from a stored proc. or DB schema, and then trick it up by hand. There are tricks you can do for frequently changing DB schemas ...

This XSD file is then run against XSD.exe, in the framework (same in VS.NET), to produce a class which is the strongly typed DataSet that we code against. That code should not be modified, because if you change the XSD, and then regen the class, your modifications will be lost (more about this later). In general, most of what you specify in the XSD file will be transferred over to the generated class:

NOTE: right click on the XSD file and select 'Preview DataSet' to see what the resulting object will look like

It actually generates a ton of code, i do not recommend printing that class out ... for the tree huggers. If you want to see the class, click on the 'Show All Files' above the solution of your project in VS.NET. This will show a '+' symbol next to the XSD file. Expanding that will show 2 more files. The 1st file (.cs or .vb) is the class that is generated. The 2nd file (.xsx) saves the layout of the XSD elements for the designer. BUG: For fun, create an XSD file and drag an element from the Toolbox on it. Then name that element 'Data', and generate your DataSet. It has a namespace conflict between MyProject.MyDataSet.DataRow and System.DataRow

Hey, where are my enumeration facets?

MyTypedDataSet.xsd | MyTypedDataSet.cs

When defining XSD, you can define what are called Enumeration facets. They are exactly what you expect, a list of valid values for a type. So if I have a Calendar element with an attribute for 'day'. Then it would have a facet defined like this:

<xs:simpleType name="MyDayType">
	<xs:restriction base="xs:string">
		<xs:enumeration value="Monday" />
		<xs:enumeration value="Tuesday" />
		<xs:enumeration value="Wednesday" />
		<xs:enumeration value="Thursday" />
		<xs:enumeration value="Friday" />
		<xs:enumeration value="Saturday" />
		<xs:enumeration value="Sunday" />
	</xs:restriction>
</xs:simpleType>

These can be defined a couple of diff ways using VS.NET:

With the enum defined in the XSD, when you gen the Typed DataSet, go and look at the generated class. You would expect for it to have created enums in the class, but that info is nowhere to be found. But you're like, 'Hey, I read the Richter book, check the IL!'; and sadly it is not there either (MS Press is cranking out the best books after their B2 MSDN reprint folly; Wrox has thoroughly let me down, save the early adopter series).The Calendar element day attribute is now defined as a string, instead of the 'MyDayType' which was defined above, so you can write code like the following and it will not fail at runtime

	MyTypedDS.MyTypedDataSet mtds = new MyTypedDS.MyTypedDataSet();
	mtds.EnforceConstraints = true;
	mtds.MyTable.AddMyTableRow("blahString", "blahColor", "blahDay");
	mtds.AcceptChanges();
	mtds.WriteXml("mtds.xml");

And now you're just pissed, because this is reminding you of your Java days when you didnt have enums. Which is why you are not doing J# now :) im just building up the problem before getting to the solution

Solution

MainApp.cs | TdsEnum.cs | MyEnum.cs
  MyTypedDataSet.cs.enum.cs (CodeDom generated)

So we lose some of our strong typing, but we can get it back. We just have to be able to think XML, Relationally, and (Object)-ively ... intended. The XSD has all the enum info we need, we just have to be able to parse it out. Also, the generated MyTypedDataSet class can be inherited from by a MyStrongerTypedDataSet class, and the enum info can be added there. That is great if we all had interns to do grunt work for us, but it is still a maintenance issue for when the XSD changes. That is where CodeDom steps in so that this can be automated (and make this a cool article ... hopefully). Here is the logic in pseudo code

	//SET
	EnumMyTypedDataSet emtds = new EnumMyTypedDataSet();
	//color was defined local (BUG), day was defined global
	emtds.MyTable.AddMyTableRow("blahString", "Color", EnumMyTypedDataSet.MyDayType.Sunday.ToString());
	//GET
	string strRep = emtds.MyTable[0].MyDay;
	EnumMyTypedDataSet.MyDayType mdt = EnumMyTypedDataSet.ParseMyDayType(strRep);

It is a console app which accepts a project file as an argument, so just create a shortcut of it on your desktop. Then drag and drop a .csproj file with Typed DataSets and XSD files definining enum facets to generate the Stronger Typed DataSet(s). The methods called are:

 retVal = (CategoryType) System.Enum.Parse(typeof(CategoryType), strRep, true);

Source

This is a .cs specific implementation, it could be made generic to handle .vb as well. It is not production quality (developed in my spare time, although i have a need for it at work), the motto being 'information wants to be free, exception handling costs money'

StrongerTDS C# Source Code

Future

When ICodeParset is implemented, this could be extended to more tightly integrate the generated enums and override the System.String typing. Typed DataSets will likely have enums baked in by MS in the future, probably the same time we get an ICodeParser implementation :) A requirement for a buddy is that he needs the fixed length XSD restriction as well. So that if he defines an attrib of type string with a fixed length of 50 (for DB mapping), then he wants the in-memory Typed DataSet to handle that and throw an exception if the length is violoated. XSD will become king for generating domain objects ... the cry I have heard is 'The Schema is the API!' As far as my next article ... unknown, holding my breath for the compact framework beta; just finished the J# early adopter and am going through the Java Standard Tag Library early adopter right now ... my nick being kc# formerly known as kcj [java] (formerly known as kc++)