Yesterday I described how to create a Custom BuildProvider, that automatically creates a string class with static string members from an xml file, containing localized text for a webpage.
I wanted to generalize the class, so that i could use it for different purposes, for instance an "Urls" class to contain all site urls defined also in an xml file.
I decided to define what to generate in the builder.stringsbuildprovider file, that also triggers the codegeneration like so:
<?xml version="1.0" encoding="utf-8" ?> <!-- Builds static key/value "strings" classes from the defined values filename: which XML file contains the data classname: define the name of the class you want namespace: in which namespace should the class be created nodename: what is the single node name that defines the data attributename: which name from xml attribute do you want as the lookupvalue valuename: which attribute from xml contains the value --> <classes> <class filename="~/language/da-DK/Resource.xml" classname="Strings" namespace="Amino.Børs" nodename="Resource" attributename="name" valuename="name"/> <class filename="~/SiteUrls.xml" classname="Urls" namespace="Amino.Børs" nodename="SiteUrls/location" attributename="name" valuename="path"/> </classes>
Using the following class:
/// <summary> /// This class builds a class with static /// strings with specifications provided /// in the builder.stringsbuildprovider file /// </summary> class StringsBuildProvider : BuildProvider { public override void GenerateCode(AssemblyBuilder assemblyBuilder) { CodeCompileUnit generated = GenerateUnit(base.VirtualPath); assemblyBuilder.AddCodeCompileUnit(this, generated); base.GenerateCode(assemblyBuilder); } private CodeCompileUnit GenerateUnit(string virtualPath) { CodeCompileUnit unit = new CodeCompileUnit(); // Load the builderfile XmlDocument d = new XmlDocument(); using (Stream s = VirtualPathProvider.OpenFile(virtualPath)) { d.Load(s); } /// We iterate the builder file and process all class entries /// turning them into classes in the wanted namespace /// with the content of the resource file as static strings foreach (XmlNode builder in d.SelectSingleNode("classes")) { CodeNamespace nspace = new CodeNamespace(builder.Attributes ["namespace"].Value); unit.Namespaces.Add(nspace); // prepare the class CodeTypeDeclaration cls = new CodeTypeDeclaration(builder.Attributes ["classname"].Value); // Load the file XmlDocument map = new XmlDocument(); using (Stream s = File.Open(ABContext.Current.MapPath(builder.Attributes ["filename"].Value), FileMode.Open)) { d.Load(s); } /// Run through each item in the file, and create the static string field /// and add it to the codetypedeclaration foreach (XmlNode n in d.SelectSingleNode(builder.Attributes["nodename"].Value)) { if (n.NodeType != XmlNodeType.Comment) { cls.Members.Add(CreateField(n.Attributes[builder.Attributes ["attributename"].Value].Value, n.Attributes[builder.Attributes["valuename"].Value].Value)); } } /// add the codetypdeclaration to the namespace, which was added to the unit nspace.Types.Add(cls); } // we return the unit / generated code return unit; } private CodeMemberField CreateField(string fieldName, string valuename) { // Create a string with the given name CodeMemberField field = new CodeMemberField(typeof(string), string.Format("{0}", fieldName)); // Make it public and static field.Attributes = MemberAttributes.Public | MemberAttributes.Static; // Set the value field.InitExpression = new CodeSnippetExpression(string.Format("\"{0}\"", valuename)); return field; } }
In the process I ran into some problems, where the classes were not available to me. My output window during compilation hinted som builderrors, so I set a breakpoint at the GenerateCode method, but it didn't hit it, which makes sense.
But the question now was, how do i debug a buildprovider? Well the only answer I could think of, was to launch another instance of Visual Studio with the same solution and setting the breakpoint there and then attach the debugger to the original instance of Visual Studio that is going to build the project - and then build.
And it works, although not very practical. Watch out that you don't overwrite your changes from the second Visual Studio when editing in the first.
Remember Me