XSD -> Classes Generator Custom Tool (radically new and extensible)
Note: this entry has moved.
This article is a companion to
Code Generation in the .NET Framework Using XML Schema article published in the
MSDN XML DevCenter. In that article, I introduce you to the main classes in
the .NET framework that enable code generation from XSD
files, the very clases the XSD.EXE tool uses. The ideas come
from my initial post on Customizing XSD->Classes code generation, the "easy"
way.
In this article I'll show how the VS.NET custom tool
included in the MSDN article code download is created.
Building the XsdCodeGen Custom Tool
Creating a custom tool for VS.NET is extremely easy.
Microsoft has released a project with all the necessary
classes for that purpose, which can be downloaded from
GotDotNet
(http://www.gotdotnet.com/Community/UserSamples/Details.aspx?SampleGuid=4AA14341-24D5-45AB-AB18-B72351D0371C).
Everything we need to do is create a new class library
project using that one as a skeleton, and add a new class
inheriting from the
BaseCodeGeneratorWithSite provided:
The generator is registered as a COM component, and that’s why we need to specify the GuidAttribute and the ComVisibleAttribute. The former should receive a new GUID which can be created from the Tools -> Create GUID menu. We call it a generator because that’s the registry key they are registered under. That key has two sub keys, one for the VB.NET and another for the C# generators. We should register our tool with both, as we’re using the language-agnostic CodeDom. Therefore, we define two constants that hold the GUIDs of such categories:
We’ll also store the GUID assigned to the class itself for easy access, as we’ll need it for registration. We also define the custom tool name and description with constants. The former is important as it’s the one that is assigned to a file through the Property Browser in order to execute the tool:
The registry key we need to create looks like the following:
As we don’t have any dependencies on any VS version, we should register under both keys, “7.0” and “7.1”, replacing the {Version} part. Also, we will register for both language categories. We will store as a constant the full registry key skeleton to format with the variable parameters. The registration methods are:
Finally, we have to call these methods at COM
registration/unregistration time. We can specify the methods
to be executed at that time, through the
ComRegisterFunctionAttribute/ComUnregisterFunctionAttribute
attributes:
After compiling the project, we need to run the following command (in a VS.NET command prompt) on the project output folder to register the custom tool:
And to unregister it:
Alternatively, you can go to the project properties dialog,
under Configuration Properties > Build, and set Register
for COM Interop to true.
To run the custom tool for a file, we just need to set its
Custom Tool property to
XsdCodeGen.
Now we can finally move to the actual code generation inside
the tool.
Generating Code
The first step is to factor out the code we used for the
console application. We will associate the custom tool with
XSD files, so we will already have the source filename in
place. We won’t need the language because the
BaseCodeGeneratorWithSite base class we used
already provides access to a
CodeDomProvider that is the appropriate one
according to the current project type where the file lives
in. Finally, as we specified at tool registration time that
we would generate source at design-time (GeneratesDesignTimeSource
registry value), VS.NET will automatically generate a new
file dependent on the schema file, much like it does with a
TypedDataset schema and its corresponding class
file, a WebForm and its code-behind, a WinForm and its
resource file, etc. All we need is implement the following
method from the tool base class:
In order to better reuse code from the custom tool and the
console app, we will separate the XSD processing in a new
class and we will make the console app use this one by
referencing the custom tool project. We could also create an
entirely new class library just for this purpose, too.
So, the GenerateCode() method implementation
is:
The new Processor class has almost the same
code as the console app, but only generates the
CodeNamespace, leaving the responsibility of
generating the final source form to the caller. Note that
the base class for the tool already provides access to the
appropriate ICodeGenerator instance we
mentioned, through the GetCodeWriter() method.
We can also access the
Custom Tool Namespace property that appears in
the Property Browser for a file, through the
FileNameSpace property.
We’re done with the tool now. We can start using it right
away by associating the XsdCodeGen custom tool
with any WXS file. Note that VS.NET locks the assembly when
the tool is run for the first time. Should we want to keep
modifying it and re-compiling, we will have to restart the
IDE.
Extensions for codegen can be easily created to leverage the tool, simply by placing the extension assembly next to the custom tool installation folder, and adding the extension to the schema as follows:
<?xml version="1.0" encoding="utf-8" ?>
<xs:schema
...etc...> <xs:annotation>
<xs:appinfo> <Code xmlns= "http://weblogs.asp.net/cazzu">
<Extension Type="XsdGenerator.Extensions.ArraysToCollectionsExtension, XsdGenerator.Library" />
<Extension Type="XsdGenerator.Extensions.FieldsToPropertiesExtension, XsdGenerator.Library" />
<Extension Type="MyExtensions.DoSomethingCool, MyExtension" />
</Code>
</xs:appinfo>
</xs:annotation>
You'll need those extensions in order to modify the default output that matches the xsd.exe one. Enjoy!