Stumbling Through: Visual Studio 2010 (Part III)
The last post ended with us just getting started on stumbling into text template file customization, a task that required a Visual Studio extension (Tangible T4 Editor) to even have a chance at completing. Despite the benefits of the Tangible T4 Editor, I still had a hard time putting together a solid text template that would be easy to explain. This is mostly due to the way the files allow you to mix code (encapsulated in <# #>) with straight-up text to generate. It is effective to be sure, but not very readable. Nevertheless, I will try and explain what was accomplished in my custom tt file, though the details of which are not really the point of this article (my way of saying dont criticize my crappy code, and certainly dont use it in any somewhat real application. You may become dumber just by looking at this code. You have been warned really the footnote I should put at the end of all of my blog posts). To begin with, there were two basic requirements that I needed the code generator to satisfy: Reading one to many entity framework files, and using the entities that were found to write one to many class files. Thankfully, using the Entity Object Generator as a starting point gave us an example on how to do exactly that by using the MetadataLoader and EntityFrameworkTemplateFileManager you include references to these items and use them like so: // Instantiate an entity framework file reader and file writer MetadataLoader loader = new MetadataLoader(this); EntityFrameworkTemplateFileManager fileManager = EntityFrameworkTemplateFileManager.Create(this); // Load the entity model metadata workspace MetadataWorkspace metadataWorkspace = null; bool allMetadataLoaded =loader.TryLoadAllMetadata("MFL.tt", out metadataWorkspace); EdmItemCollection ItemCollection = (EdmItemCollection)metadataWorkspace.GetItemCollection(DataSpace.CSpace); // Create an IO class to contain the 'get' methods for all entities in the model fileManager.StartNewFile("MFL.IO.gen.cs"); Next, we want to be able to loop through all of the entities found in the model, and then each property for each entity so we can generate classes and methods for each. The code for that is blissfully simple: // Iterate through each entity in the model foreach (EntityType entity in ItemCollection.GetItems<EntityType>().OrderBy(e => e.Name)) { // Iterate through each primitive property of the entity foreach (EdmProperty edmProperty in entity.Properties.Where(p => p.TypeUsage.EdmType is PrimitiveType && p.DeclaringType == entity)) { // TODO: Create properties } // Iterate through each relationship of the entity foreach (NavigationProperty navProperty in entity.NavigationProperties.Where(np => np.DeclaringType == entity)) { // TODO: Create associations } } There really isnt anything more advanced than that going on in the text template the only thing I had to blunder through was realizing that if you want the generator to interpret a line of code (such as our iterations above), you need to enclose the code in <# and #> while if you want the generator to interpret the VALUE of code, such as putting the entity name into the class name, you need to enclose the code in <#= and #> like so: public partial class <#=entity.Name#> To make a long story short, I did a lot of repetition of the above to come up with a text template that generates a class for each entity based on its properties, and a set of IO methods for each entity based on its relationships. The two work together to provide lazy-loading for hierarchical data (such getting Team.Players) so it should be pretty intuitive to use on a front-end. This text template is available here you can tweak the inputFiles array to load one or many different edmx models and generate the basic xml IO and class files, though it will probably only work correctly in the simplest of cases, like our MFL model described in the previous post. Additionally, there is no validation, logging or error handling which is something I want to handle later by stumbling through the enterprise library 5.0. The code that gets generated isnt anything special, though using the LINQ to XML feature was something very new and exciting for me I had only worked with XML in the past using the DOM or XML Reader objects along with XPath, and the LINQ to XML model is just so much more elegant and supposedly efficient (something to test later). For example, the following code was generated to create a Player object for each Player node in the XML: return from element in GetXmlData(_PlayerDataFile).Descendants("Player") select new Player { Id = int.Parse(element.Attribute("Id").Value) ,ParentName = element.Parent.Name.LocalName ,ParentId = long.Parse(element.Parent.Attribute("Id").Value) ,Name = element.Attribute("Name").Value ,PositionId = int.Parse(element.Attribute("PositionId").Value) }; It is all done in one line of code, no looping needed. Even though GetXmlData loads the entire xml file just like the old XML DOM approach would have, it is supposed to be much less resource intensive. I will definitely put that to the test after we develop a user interface for getting at this data. Speaking of the data where IS the data? Weve put together a pretty model and a bunch of code around it, but we dont have any data to speak of. We can certainly drop to our favorite XML editor and crank out some data, but if it doesnt totally match our model, it will not load correctly. To help with this, Ive built in a method to generate xml at any given layer in the hierarchy. So for us to get the closest possible thing to real data, wed need to invoke MFL.IO.GenerateTeamXML and save the results to file. Doing so should get us something that looks like this: <Team Id="0" Name="0"> <Player Id="0" Name="0" PositionId="0"> <Statistic Id="0" PassYards="0" RushYards="0" Year="0" /> </Player> </Team> Sadly, it is missing the Positions node (havent thought of a way to generate lookup xml yet) and the data itself isnt quite realistic (well, as realistic as MFL data can be anyway). Lets manually remedy that for now to give us a decent starter set of data. Note that this is TWO xml files Lookups.xml and Teams.xml: <Lookups Id=0> <Position Id="0" Name="Quarterback"/> <Position Id="1" Name="Runningback"/> </Lookups> <Teams Id=0> <Team Id="0" Name="Chicago"> <Player Id="0" Name="QB Bears" PositionId="0"> <Statistic Id="0" PassYards="4000" RushYards="120" Year="2008" /> <Statistic Id="1" PassYards="4200" RushYards="180" Year="2009" /> </Player> <Player Id="1" Name="RB Bears" PositionId="1"> <Statistic Id="2" PassYards="0" RushYards="800" Year="2007" /> <Statistic Id="3" PassYards="0" RushYards="1200" Year="2008" /> <Statistic Id="4" PassYards="3" RushYards="1450" Year="2009" /> </Player> </Team> </Teams> Ok, so we have some data, we have a way to read/write that data and we have a friendly way of representing that data. Now, what remains is the part that I have been looking forward to the most: present the data to the user and give them the ability to add/update/delete, and doing so in a way that is very intuitive (easy) from a development standpoint.Did you know that DotNetSlackers also publishes .net articles written by top known .net Authors? We already have over 80 articles in several categories including Silverlight. Take a look: here.