The Code Generation Guidance Package on CodePlex was designed to be extensible and reusable. There are three main C# projects, which isolate logic for code generation, and the UI and GAX components.
This is the core project, defining how metadata is represented and retrieved, and defining configuration for code generation runs. Metadata is defined in terms of:
- A metadata source, which contains the logic for connecting to the source
- A metadata provider, which uses the source to provide a list of available items, and to populate requested items
- Metadata items, which are a representation of the metadata, typically using an appropriate .NET framework class
Code generation and UI logic always works on the interfaces which define these components, and the relationships between them:
In the Add Generated Items recipe, the available source types, sources and providers are populated from the collection of IMetadataSource objects held in the static SourceContainer class (additional sources can be added to the container at runtime using the SourceAssemblyConfiguration recipe argument – described in Sixeyed.Guidance.CodeGeneration Sample Package). When a provider is selected, GetItemNames is called to populate the list of available metadata items. When the selected items are iterated over in a code generation run, GetItem is called and the populated IMetadataItem is loaded as a property for the T4 template. The metadata item exposes the provider, and the provider exposes the source so the template can connect directly to the metadata if it needs to.
Each metadata interface has an abstract base class which implements it and contains core logic. For a given source, there may be an additional base class which abstracts logic common to that type of source. In Sixeyed.CodeGeneration.Metadata.Database.Source, DatabaseSource contains logic for accessing ADO.NET data sources in terms of IDbConnection and IDbCommand objects; SqlServerSource contains overriding logic to specify SqlConnection and SqlCommand usage – any additional database sources would do the same. Similarly for the SOAP metadata source, the logic for accessing WSDL is abstracted into a base class which may be used by other sources (e.g. a WCF specific source):
IMetadataProvider contains logic for accessing the source and retrieving a list of available items, and for populating a named item. The majority of logic is in the generic base class, so provider classes are quite simple. Simpler still are metadata item classes, which inherit the generic ItemBase class, specifying the underlying type of the item (XmlSchema in this case), without requiring any additional code.
Logic for populating template properties and executing T4 templates is located in the Guidance project rather than the code generation project, as it is dependent on the specific T4 engine being used.
The UI project contains generic user controls which represent metadata selections, and expose various events to notify of selection changes. The Guidance package Wizard uses these components rather than defining its own UI – the controls from this assembly allow code generation to be configured and invoked from a different UI host.
This project defines the GAX package and links together the code generation and UI components. The Guidance package is quite straightforward – it contains a single class AddGeneratedItems for the action of generating items from metadata and adding them to the selected project. The action expects SourceConfiguration, SelectedItems and TemplateConfiguration arguments; it connects to the source, iterates over the selected items and executes the template for each item.
To collect the argument values, there are custom wizard pages and value providers for each, allowing a recipe to provide ready-configured settings, or build a UI to gather them from the user:
- Sixeyed.Guidance.CodeGeneration.CustomWizardPages. The custom Wizard pages use the static Metadata class to share state for the configured metadata source and provider. They override IsDataValid to determine if the page has collected enough information for the Wizard to proceed and the base class provides EnableNextStep which will advance the Wizard by enabling the Next or Finish button as appropriate. The base class will read the SourceAssemblyConfiguration recipe argument if provided, and use it to add any extra metadata assemblies at runtime.
- Sixeyed.Guidance.CodeGeneration.ValueProviders. The value provider classes are straightforward, reading optional arguments from the recipe and populating the relevant metadata settings. Custom recipes can specify value providers to load settings instead of Wizard pages, so the Wizard UI can be restricted.
There are a couple of minor changes I’d like to make to the current codebase:
- Sixeyed.Guidance.CodeGeneration.CustomWizardPages.SelectTemplate defines its own UI, which should be moved to a user control in the UI project;
- Sixeyed.CodeGeneration.Generation.MemberName contains methods for providing valid field and property names, but the Pascal and camel-case conversion is very basic;
- Sixeyed.CodeGeneration.UI.SourceUri has some ugly hard-coding to decide which UI dialog it should show for different types of URI. It should be configurable, but I’m not happy moving it to an attribute of IMetadataSource, as that brings the UI into the core project.
And some additional metadata sources which I’ll add at a future point – a Clipboard source providing access to clipboard data through text or XML providers, and a BizTalk source which will use the ExplorerOM for providers supplying application, group and instance information as metadata.