Generate code with the BuildProvider

Now and then you find yourself between a rock and a hard place in the attempt to make your code easy to understand, and easily maintainable at the same time.

I am currently working on a fairly comprehensive refactoring of a site, or more accurately, a series of similar sites. Amongst other things I have implemented the possibility of localizing these sites.

The localized text is placed in Resource.xml files, that are handled by a custom ResourceManager, and looks like this:

<?xml version="1.0" encoding="utf-8" ?> <Resource> <!-- General terms --> <item name="Wanted">Købes</item> <item name="For_Sale">Sælges</item> <item name="Merge">Fusion</item> </Resource>

On the basepage i have a GetString method that uses the ResourceManager to get the localized text. I works nicely. No locked files, no sattelite assemblies and very transparent.

But one thing bugged me a little. All the strings were kind of magical, and hidden in the xml files - I like intellisense on everything for speed, accuracy and transparency. But on the other hand, I don't want to manually add all texts to a Strings class as well.

So i wrote a little simple code-generation. It is an inherited BuildProvider, which in a rather simple way reads your language file, and creates a Strings class with all the string references.

Heres the code. Its a first draft, so it might need a little work.

public class StringsBuildProvider : BuildProvider { public override void GenerateCode(AssemblyBuilder assemblyBuilder) { string fileName = base.VirtualPath; CodeCompileUnit generated = GenerateUnit(fileName); assemblyBuilder.AddCodeCompileUnit(this, generated); } private CodeCompileUnit GenerateUnit(string fileName) { CodeCompileUnit unit = new CodeCompileUnit(); CodeNamespace nspace = new CodeNamespace("Amino.Børs"); unit.Namespaces.Add(nspace); CodeTypeDeclaration classDeclaration = CreateClass(); nspace.Types.Add(classDeclaration); return unit; } private CodeTypeDeclaration CreateClass() { string file = ABContext.Current.MapPath("~/language/da-DK/resource.xml"); CodeTypeDeclaration cls = new CodeTypeDeclaration("Strings"); XmlDocument d = new XmlDocument(); d.Load(file); foreach (XmlNode n in d.SelectSingleNode("Resource")) { if (n.NodeType != XmlNodeType.Comment) { cls.Members.Add(CreateField(n.Attributes["name"].Value)); } } return cls; } private CodeMemberField CreateField(string fieldName) { CodeMemberField field = new CodeMemberField(typeof(string), string.Format("{0}", fieldName)); field.Attributes = MemberAttributes.Public | MemberAttributes.Static; field.InitExpression = new CodeSnippetExpression(string.Format("\"{0}\"", fieldName)); return field; } }

Then you just add it to the buildProviders section in the web.config. As i have several xml files i decided to create a custom extension as singular match for the buildprovider, and simply added an empty file called builder.stringsbuildprovider.

<buildProviders> <add extension=".stringsbuildprovider type="Amino.Bors.CodeGeneration.StringsBuildProvider, Amino.Bors"/> </buildProviders>

So now i have all my resource reference strings at hand at all times without having to open the xml document.

Posted October 26, 2007 by Joachim Lykke Andersen
In

Comments [0]   
All comments require the approval of the site owner before being displayed.
OpenID
Please login with either your OpenID above, or your details below.
Name
E-mail
(will show your gravatar icon)
Home page

Comment (HTML not allowed)  

Enter the code shown (prevents robots):

Live Comment Preview