Showing posts with label Visual Studio. Show all posts
Showing posts with label Visual Studio. Show all posts

Using MsDeploy publish profile .pubxml to create an empty folder structure on IIS and skip deleting it with MsDeploySkipRules

I’m going to share here a pretty nice deployment automation solution for when you need to have a defined set of folders in place when first deploying an app to IIS. On subsequent re-deployments the folder structure will be kept intact with any files users may have added to them.

Let’s work with a simple example: given an ASP.NET MVC app, we need a folder called Files and inside this folder there will be some pre-defined folders named: Folder 1, Folder 2 and Folder 3 with a child folder called Test. Something like this:

App root
|
---Files
   |
   ---Folder 1
   ---Folder 2
   ---Folder 3
      |
      ---Test

When deploying for the 1st time these folders are empty but the folder structure is mandatory let’s say because I’m using a file manager like elFinder.Net that expects that these folders exist on the server. Why? Because the ASP.NET MVC app has links pointing to these folders in some views. The folders should be ready to store files when the app is released. What also comes to my mind is the case where we need an existing Downloads/Uploads folder.

What’s more? We also want all this to happen while using Publish Web command from within Visual Studio and still keeping the option Remove additional files at destination checked:

Figure 1 - Visual Studio Publish Web with File Publish Options => Remove additional files at destinationFigure 1 - Visual Studio Publish Web with File Publish Options => Remove additional files at destination

This setting is nice because when you update jQuery NuGet package for example (jquery-2.1.1.js) it will send the new files to IIS server and will remove the old version (jquery-2.1.0.js) that exists there. This is really important so that the app keeps working as expected and don’t load the wrong version/duplicate files. If we don’t check that option we have to go to the server and delete the old files manually. What a cumbersome and error prone task!

What to do in this case where we want the deployment task do the work “automagically” for us with no human intervention? It’s seems like a lot of requirements and a task not so simple as “it appears to be”… Yep, it requires a little bit of MsDeploy codez.

Here’s what is working for me at the moment after finding some code pieces from here and there:

Given a  publish profile named Local.pubxml that sits here:
C:\Company\Company.ProjectName\Company.ProjectName.Web\Properties\
PublishProfiles\Local.pubxml

Let’s add the code blocks necessary to make all the requirements come to life:

<?xml version="1.0" encoding="utf-8"?>
<!--
This file is used by the publish/package process of your Web project. You can customize the behavior of this process
by editing this MSBuild file. In order to learn more about this please visit http://go.microsoft.com/fwlink/?LinkID=208121. 
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
<AfterAddIisSettingAndFileContentsToSourceManifest>AddCustomSkipRules</AfterAddIisSettingAndFileContentsToSourceManifest>
    <WebPublishMethod>MSDeploy</WebPublishMethod>
    <LastUsedBuildConfiguration>Local</LastUsedBuildConfiguration>
    <LastUsedPlatform>Any CPU</LastUsedPlatform>
    <SiteUrlToLaunchAfterPublish />
    <ExcludeApp_Data>False</ExcludeApp_Data>
    <MSDeployServiceURL>localhost</MSDeployServiceURL>
    <DeployIisAppPath>SuperCoolAwesomeAppName</DeployIisAppPath>
    <RemoteSitePhysicalPath />
    <SkipExtraFilesOnServer>False</SkipExtraFilesOnServer>
<MSDeployPublishMethod>WMSVC</MSDeployPublishMethod> <AllowUntrustedCertificate>True</AllowUntrustedCertificate>
<EnableMSDeployBackup>False</EnableMSDeployBackup> <UserName /> <_SavePWD>False</_SavePWD> <LaunchSiteAfterPublish>True</LaunchSiteAfterPublish> </PropertyGroup> <PropertyGroup> <UseMsDeployExe>true</UseMsDeployExe> </PropertyGroup> <Target Name="CreateEmptyFolders"> <Message Text="Adding empty folders to Files" /> <MakeDir Directories="$(_MSDeployDirPath_FullPath)\Files\Folder 1" /> <MakeDir Directories="$(_MSDeployDirPath_FullPath)\Files\Folder 2" /> <MakeDir Directories="$(_MSDeployDirPath_FullPath)\Files\Folder 3\Test"/> </Target> <Target Name="AddCustomSkipRules" DependsOnTargets="CreateEmptyFolders"> <Message Text="Adding Custom Skip Rules" /> <ItemGroup>
      <MsDeploySkipRules Include="SkipFilesInFilesFolder">
        <SkipAction>Delete</SkipAction>
        <ObjectName>filePath</ObjectName>
        <AbsolutePath>$(_DestinationContentPath)\\Files\\.*</AbsolutePath>
        <Apply>Destination</Apply>
      </MsDeploySkipRules>

      <MsDeploySkipRules Include="SkipFoldersInFilesFolders">
        <SkipAction></SkipAction>
        <ObjectName>dirPath</ObjectName>
        <AbsolutePath>$(_DestinationContentPath)\\Files\\.*\\*</AbsolutePath>
        <Apply>Destination</Apply>
      </MsDeploySkipRules>

</ItemGroup>
</
Target> </Project>

This is self explanatory. Pay attention to the highlighted parts as they are the glue that make all the requirements happen during the publish action.

What is going on?

The property

<AfterAddIisSettingAndFileContentsToSourceManifest>
AddCustomSkipRules
</AfterAddIisSettingAndFileContentsToSourceManifest>

calls the target

<Target Name="AddCustomSkipRules"
DependsOnTargets="CreateEmptyFolders">
that in turn depends on the other task
<Target Name="CreateEmptyFolders">

CreateEmptyFolders take care of adding/creating the folder structure on the server if it doen’t exist yet.

AddCustomSkipRules contains two  <MsDeploySkipRules...>. One is to prevent deleting Files and the other prevents deleting the child folders.

Check the targets’ logic. They’re pretty easy to understand…

Note: make sure you don’t forget the

<UseMsDeployExe>true</UseMsDeployExe>

otherwise you may see this error during deployment:

Error    4    Web deployment task failed. (Unrecognized skip directive 'skipaction'. Must be one of the following: "objectName," "keyAttribute," "absolutePath," "xPath," "attributes.<name>.")        0    0    Company.ProjectName.Web

Simple as pie after we see it working. Isn’t it? Winking smile

Hope it helps!

As an interesting point, see the command executed by msdeploy.exe that gets written to the output window in Visual Studio:

C:\Program Files (x86)\IIS\Microsoft Web Deploy V3\msdeploy.exe -source:manifest='C:\Company\Company.ProjectName\Company.ProjectName.Web\obj\
Local\Package\Company.ProjectName.Web.SourceManifest.xml' -dest:auto,IncludeAcls='False',AuthType='NTLM' -verb:sync -disableLink:AppPoolExtension -disableLink:ContentExtension -disableLink:CertificateExtension -
skip:skipaction='Delete',objectname='filePath',absolutepath='\\Files\\.*' -skip:objectname='dirPath',absolutepath='\\Files\\.*\\*' -setParamFile:"C:\Company\Company.ProjectName\Company.ProjectName.Web\obj\Local\ Package\Company.ProjectName.Web.Parameters.xml" -retryAttempts=2 -userAgent="VS12.0:PublishDialog:WTE2.3.50425.0"

Debugging Microsoft Web Deploy Visual Studio publishing errors

This post is intended for starters with MS Web Deploy as is my case… Shifty

Microsoft Web Deploy is so awesome that I think it’s one of the best features one has at their disposal when developing with the .NET Framework and for the Web with ASP.NET MVC for example. It’s so powerful that it can even publish a database.

Once upon a time (let’s say before 2010) there was no Web Deploy and you had to FTP to the server to deploy the application. This was a tiresome task and led to many errors because one had to be extremely careful of what files needed to be updated, added, removed, etc while installing the application on the server. Backup methods should be in place in case something went wrong. I used to use compressed files (.zip or .rar) to send the app’s files to the server. It was still a big upload in some cases. I had to wait patiently. Once there I had to uncompress the file. Everything needed to be moved into place to make things easier. I even created a step by step readme file to help other peers do the deploy on the server. This readme detailed the workflow that should be followed in order to get things working.

Thanks God today there’s Web Deploy and it changed things drastically: only new or updated files are uploaded to the server. No more long waiting periods to start using the application. As a bonus you also have the option of removing additional files on the server (files/folders that aren’t used anymore). All this happens automatically (could we say automagically) with the click of a button inside Visual Studio 2010 or 2012. You can also run a .cmd file in the command line. This .cmd file is created as part of the Web Deploy Package publish method that you can choose once inside Visual Studio. There’s so many awesome features (read Preview menu option in Figure 4 bellow) that they would not fit in this post… To get a full understanding about Web Deploy look at this post: Visual Studio 2012 RC Deployment Docs.

Well, 2 weeks ago I tried to solve a problem related to Web Deploy. You can read the full history here:

Visual Studio 2012 Web Deploy to Windows Server 2008 R2 with IIS 7 and /msdeploy.axd 404 error

The above problem wasn’t solved but I can tell you that I learned a LOT about IIS administration during that weekend. As a result it was worth the effort. I think that a clean install of Windows Server 2008 is necessary in that case…

Today I was facing a different error (again working against Windows Server 2008 R2) and I followed a simple path to solve it. So here are the basic steps you need to follow to detect the problem’s origin:

Visual Studio 2012 Publish project with Web Deploy and Validate Connection errorFigure 1 - Visual Studio 2012 Publish project with Web Deploy and Validate Connection error

As you see when I clicked the Validate Connection button I got a message: ERROR COULD NOT CONNECT TO REMOTESVC.

If you click the error message, there’s some more info about the error but it doesn’t really tell you what’s causing the problem.  It’s kind of a generic message you get: go check the Web Management Service on your server… I think I don’t need to tell you that it’s started already! Steaming mad

Figure 2 - Visual Studio 2012 Publish project with detailed ERROR informationFigure 2 - Visual Studio 2012 Publish project with “detailed” ERROR information

To really get to the problem, you’ll need to access the server you’re trying to deploy to. Once there go to this folder:

C:\inetpub\logs\wmsvc\W3SVC1

This is the standard path where Web Management Service logs what’s going on…

When I opened the latest .log file I saw this:

#Software: Microsoft Internet Information Services 7.5
#Version: 1.0
#Date: 2012-07-26 15:50:30
#Fields: date time s-ip cs-method cs-uri-stem cs-uri-query s-port cs-username c-ip cs(User-Agent) sc-status sc-substatus sc-win32-status time-taken
2012-07-26 12:50:30 192.168.100.77 HEAD /msdeploy.axd site=Default%20Web%20Siste 8172 - 189.24.83.277 - 401 2 5 1918
2012-07-26 12:50:33 192.168.100.77 HEAD /msdeploy.axd site=Default%20Web%20Siste 8172 develop
189.24.83.277 - 550 0 0 2761

As you see highlighted in orange is my developer machine IP address. This let’s me know that my connection to server is working as expected. Port 8172 is not blocked by the firewall, etc.
Highlighted in yellow is a sc-status = 550. This is the key part while debugging errors related to web deploy on the server. Now it’s far easier to know what’s the problem. It’s only a matter of Googling about the 550 status code. The first post that mentions this status code was this one:

Status code 550 when using Msdeploy

The guy lost 1 day to find what was going on and it took me only 1 minute to figure out the error I had with his help. He talks about the site’s name displayed in IIS manager. So I went check mine and to my surprise I had a small mistake in the Site/application name field shown in Figure 1. I typed Default Web Siste instead of only Default Web Site. What a silly mistake!

This is the kind of error that can bother you more than it should… believe it or not. You can go the wrong way while trying to get things working as expected.

After correcting this mistyped named I clicked the Validate Connection button again and to my delight and surprise I got this beautiful green check mark:

Visual Studio 2012 Publish project with Web Deploy and successful connection validationFigure 3 - Visual Studio 2012 Publish project with Web Deploy and successful connection validation

Now if you desire select the Preview menu option to glance at what’s going to the server…

Visual Studio 2012 Publish project with Web Deploy and Previewing the changes
Figure 4 - Visual Studio 2012 Publish project with Web Deploy and Previewing the changes

You can select just the files you want to deploy. This is super useful let’s say when you discover a bug in production and want to change something in whatever file to correct that bug. You already have more changes to pull to the server but you just want to correct that bug. With the Preview tab you can do this: select just the file(s) you changed to correct the bug and deselect all the others. Isn’t this awesome? Of course it is!

If it’s OK, just hit the Publish button and you’re good to go.

I hope this simple post helps you find the root cause of what’s causing errors while you’re trying to deploy your application using MS Deploy.

References
Configuring a Web Server for Web Deploy Publishing (Remote Agent)

Installing and Configuring Web Deploy

Web Deploy error codes

Export list of used NuGet packages for a given project

This one is as simple as the title says… I just wanted to get a list of all NuGet packages I’m currently using in my ASP.NET MVC 4 project.

If you look at the NuGet Package Manager window that you can access by right clicking a project in Solution Explorer and then selecting Manage NuGet Packages… you’ll see that there’s an Installed packages option on the left vertical menu. This is good and all but the manager doesn’t have an option to export the list of installed packages to a simple .txt file.

NuGet Package Manager listing Installed Packages for the projectFigure 1 - NuGet Package Manager listing Installed Packages for the project

One interesting thing is that NuGet uses a .XML file called packages.config that resides in the root folder of every project to actually fill the above window. Every time you add or delete a NuGet package this file is updated to reflect the changes.

This is the content of my packages.config file:

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="AttributeRouting" version="1.5.4426" />
  <package id="DataAnnotationsExtensions" version="1.0.1" />
  <package id="DataAnnotationsExtensions.MVC3" version="1.0.1" />
  <package id="jQuery" version="1.7.1" />
  <package id="jQuery.Ajax.Unobtrusive" version="1.0" />
  <package id="jQuery.UI.Combined" version="1.8.17" />
  <package id="jQuery.Validation" version="1.8" />
  <package id="jQuery.Validation.Unobtrusive" version="1.0" />
  <package id="jquery-globalize" version="0.1.0" />
  <package id="knockoutjs" version="1.2.9.0" />
  <package id="log4net" version="1.2.10" />
  <package id="Microsoft.Web.Optimization" version="0.1" />
  <package id="microsoft-web-helpers" version="1.15" />
  <package id="Modernizr" version="2.0.6" />
  <package id="MvcSiteMapProvider" version="3.2.1.0" />
  <package id="Newtonsoft.Json" version="4.0.8" />
  <package id="NLog" version="2.0.0.2000" />
  <package id="RavenDB-Embedded" version="1.0.700" />
  <package id="System.Web.Providers" version="1.0.1" />
  <package id="T4MVC" version="2.7.0" />
  <package id="WebActivator" version="1.2.0.0" />
</packages>

This file serves the purpose of this post but it’d be a nice addition to the NuGet manager if it had a button to export the list of installed packages in a better formatted way. Just an idea.

Just let me take the opportunity to say “I Love You NuGet”. You let me explore the plethora of knowledge of fellow developers scattered all over the world in an easy way with the push of a button. I have no better words to describe you! You contribute enormously to the world development. Keep evolving!

Replacing Web.config settings with Transforms

First off: this is the 100th post I write in this blog. A great amount of shared info… Party smile

Now let’s say you want to point to a different connection string when you deploy your ASP.NET Web Project to your hosting provider. Until recently you’d have to modify your Web.config file manually. This is an easy procedure but you might end screwing up the file in some way.

Visual Studio 2010 comes with a great new feature called Web.config Transformation that allows you to perform transformations in whatever section of your Web.config file. This transformation process happens automatically when you build the deployment package for your application. Transformations occur as part of the MSBuild process. With this it’s easy to change the connection string (or better yet, anything you want) depending on the configuration currently selected when you build the deployment package. No more Web.config manual editing. Thanks God!

This post shows a simple replace transformation in which I replace one of my app settings named Connection.

This is the actual code of my Web.config file:

<appSettings>
<!-- Database connection pointer -->
<add key="Connection" value="MyProject.Web.Settings.local" />
</appSettings>
<connectionStrings>
<
add name="MyProject.Web.Settings.local" connectionString="Server=localhost\sqlexpress;Database=MyDatabase;Integrated Security=True;" providerName="System.Data.SqlClient" />
<
add name="MyProject.Web.Settings.hostingProvider" connectionString="Server=123.45.678.9;Database=MyDatabase;UID=user;PWD=pass123;MultipleActiveResultSets=true;Asynchronous Processing=True;" providerName="System.Data.SqlClient" />
</connectionStrings>

Step 1 - Create a new Configuration named Test:

Visual Studio Configuration Manager (menu Build => Configuration Manager)Figure 1 - Visual Studio Configuration Manager (menu Build => Configuration Manager)

Creating a New ConfigurationFigure 2 - Creating a New Configuration

New Solution Configuration named TestFigure 3 - New Solution Configuration named Test

Step 2 - Right-click your project’s Web.config file and select Add Config Transforms:

Adding Config Transforms for the Web.config fileFigure 4 - Adding Config Transforms for the Web.config file

Now you’ll see that VS creates one Web.config file for each configuration we have defined. Now we have 3 child .config files/transforms. In this case Web.Debug.config and Web.Release.config are related to the standard configurations that are automatically created with new web projects. Web.Test.config is related to the Test configuration we created in Step 1.

Web.config transforms backing filesFigure 5 - Web.config Transforms backing files

Step 3 - Double click Web.Test.config and add this code:

<?xml version="1.0"?>

<!-- For more information on using web.config transformation visit http://go.microsoft.com/fwlink/?LinkId=125889 -->

<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
    <appSettings>

        <!-- Database connection pointer -->
        <add key="Connection" value="MyProject.Web.Settings.hostingProvider" xdt:Transform="Replace" xdt:Locator="Match(key)" />

    </appSettings>

</configuration>

Above I’m transforming the Web.config file so that when I build my deployment package for the Test configuration I have the Connection appsetting replaced. My Connection appsetting will then point to the hostingProvider connection string. Take a special look in the transform I’m using (Replace) and the Locator to match the key Connection. For a more detailed explanation about the available transforms, read the post Web Deployment: Web.Config Transformation by Vishal Joshi.

Step 4 - Make sure you have selected the Test configuration in Visual Studio. Now Create the deployment package by right-clicking your ASP.NET Web Project and select Build Deployment Package:

ASP.NET Web Project’s Build Deployment Package context menu optionFigure 6 - ASP.NET Web Project’s Build Deployment Package context menu option

When you do this Visual Studio 2010 will generate a package with all the necessary files your app needs to run. My package is created in this path:

C:\MyProject\trunk\MyProject\MyProject.Web\obj\Test\Package

Inside this folder you’ll find a .zip file named MyProject.Web.zip (the package) that you can use to install the app in IIS or you can handle it to the one responsible for actually installing the app on the server.

Note: Visual Studio 2010 comes with other great feature that allows you to Publish this package directly to your hosting provider using Web Deploy, FTP, etc. I’ll cover this in another post…

You’ll also find a folder named PackageTmp. This folder has everything that goes into that .zip package. Open the Web.config file that lies within this folder and see if the transformation was applied correctly. It should’ve been applied.

In my case in my main Web.config file I’m pointing to a local connection string:

<add key="Connection" value="MyProject.Web.Settings.local" />

This makes sense since I’m working in my home machine. The transformation I'm applying should transform/replace the Connection appsetting when I build the deployment package for the Test configuration. Now the appsetting must point to the hosting provider connection string like this:

<add key="Connection" value="MyProject.Web.Settings.hostingProvider" />

Hope this simple sample helps shed some light in this really powerful and interesting feature that comes with Visual Studio 2010.

Note: if you wanna get direct access to the original and transformed Web.config files, check these folders:

C:\MyProject\trunk\MyProject\MyProject.Web\obj\Test\TransformWebConfig\original

C:\MyProject\trunk\MyProject\MyProject.Web\obj\Test\TransformWebConfig\transformed

Test transforms online
http://webconfigtransformationtester.apphb.com/

Generating code with Preprocessed T4 Text Templates

If you wanna be a T4 ninja, read all this: T4: Text Template Transformation Toolkit

Last Friday I was writing some code to populate a database with country/state information. I wrote the code to include Brazil and its 27 states but when I realized that I’d have to do the same thing again with USA and its states I thought that it’d be boring. US has 50 states and creating an object for each state would be a pain.

I have to populate the Name and Abbreviation properties of each State object. Each state is created this way in code:

new State()
    {
        Name = "Alabama",
        Abbreviation = "AL",
        Country = country2
    },

This sounded like a good a chance to try T4 Templates (Wikipedia article). T4 stands for Text Template Transformation Toolkit. That’s a beautiful name IMHO. I’ve read some articles about it since its inception but haven’t had a chance to try it. T4 can generate any text you want for whatever programming language.

T4 is used within Microsoft in ASP.NET MVC for the creation of the views and controllers, ADO.NET Entity Framework for entity generation, and ASP.NET Dynamic Data. Really powerful tool!

T4 came to my mind because I answered a question at StackOverflow 4 days ago: Is T4 template run every time it is requested? This proves that StackOverflow is a great tool to keep things fresh in your mind. The asker is creating e-mails with T4. I can imagine the creation of e-mail messages in a way that you pass a list of e-mail addresses an then for each e-mail address, T4 generates a .msg message. Just thinking..

So to start with T4 I created a simple Console Application as always and followed the steps described in this excellent MSDN step by step: Run-Time Text Generation by using Preprocessed T4 Text Templates.

To generate code to create each State object (50 in total) I created the following files within a Visual Studio Solution titled T4CodeGeneration:

T4 Code Generation Project in Solution Explorer viewFigure 1 - T4 Code Generation Project in Solution Explorer view

States’ information is stored within the USAStates.txt file that I got on the internet. This file have StateName, StateAbbreviation in each line. It’s a really basic CSV file…

Alabama,AL
Alaska,AK
Arizona,AZ
Arkansas,AR
California,CA
.
.
.

To read this simple .txt file I wrote this code (Program.cs):

class Program
{
    static void Main(string[] args)
    {
        States code = new States(ReadData());
        String result = code.TransformText();
        File.WriteAllText("outputCode.cs", result);
    }

    public static List<Tuple<string, string>> ReadData()
    {
        List<Tuple<string, string>> states = new List<Tuple<string, string>>();

        using (var myCsvFile =
                new TextFieldParser(@"C:\Users\Leniel\Documents\Visual Studio 2010\Projects\T4CodeGeneration\USAStates.txt"))
        {
            myCsvFile.TextFieldType = FieldType.Delimited;
            myCsvFile.SetDelimiters(",");

            while (!myCsvFile.EndOfData)
            {
                string[] fieldArray;

                try
                {
// Reading line data fieldArray = myCsvFile.ReadFields(); } catch (MalformedLineException) { // Not a valid delimited line - log, terminate, or ignore continue; } // Process values in fieldArray states.Add(new Tuple<string, string>(fieldArray[0], fieldArray[1])); } } return states; } }

I’m using the built in TextFieldParser class in the ReadData() method that’s part of the Microsoft.VisualBasic namespace. This class is used to read the CSV file. Take a look at the project’s references above. This class has everything one needs to read a simple text file.

I then read and add each line of the file to the states List<Tuple<string, string>>.

I use this states list within the T4 template to generate my custom code.

This is the content of the file (StatesCode.cs):

partial class States
{
    private readonly List<Tuple<string, string>> states;

    public States(List<Tuple<string, string>> states)
    {
        this.states = states;
    }
}

Interesting thing to note here is that it’s a partial class named States (this is the same name of the T4 template). During compilation this class code will be merged with the T4 accompanying States.cs file code (generated automatically by Visual Studio code generator). That’s why it’s a partial class… This class is used exclusively to pass data to the T4 template.

Now comes the really interesting part that lies within the States.tt file:

<#@ template language="C#" #>
var usaStates = new List<State>
{
<# foreach (Tuple<string, string> tuple in states) 
// states is declared in StatesCode.cs
{ #>
    new State()
    {
        Name = "<#= tuple.Item1 #>",
        Abbreviation = "<#= tuple.Item2 #>",
        Country = country2
    },
<# } // end of foreach #>
};

usaStates.ForEach(s => context.States.Add(s));

As you can see, the T4 template code has its own syntax and structures. C# code goes within <#  #> code blocks. To actually get a variable value you must place it within
<#=  #> code blocks. I’m using a foreach that traverses the states list. See that I’m accessing the Tuple<string, string> items (Item1 and Item2) within these code blocks.

Everything that lies outside <# #> is just static text and will be output as such in the generated result (code output). This is the case with the first two and last lines of T4 template code above.

Really important thing: check that you have set the T4 template CustomTool property appropriately to TextTemplatingFilePreprocessor as this is a critical part to make things work as expected:

T4 Template and Custom Tool Property configurationFigure 2 - T4 Template and Custom Tool Property configuration

Now to end result: when I run the project, the Main method (part of Program.cs above) is called:

static void Main(string[] args)
{
    States code = new States(ReadData());
    String result = code.TransformText();
    File.WriteAllText("outputCode.cs", result);
}

I read the data and pass it to the T4 template. I then call T4 TransformText() method that does the magic! Last but not least, the resulting code is written to a file called outputCode.cs.

I get a beautiful code that is placed in the Project’s Debug folder: C:\Users\Leniel\Documents\Visual Studio 2010\Projects\T4CodeGeneration\bin\Debug.

To accomplish my objective I just Copy/Paste this file code in my SampleData.cs class that I use in other project to seed my database.

As this is a pretty extensive code listing (that I’d have to type – oh my God!), I’ve chosen to let it for the final part of the post for your viewing pleasure. Open-mouthed smile See that the code is well formatted. To get code formatted you have to fight with your T4 template file.

Now that you have a basic overview of T4 I hope you have fun with it as I had. This is power in your hands. Now it’s up to you to give wings to your imagination!

Visual Studio 2010 Console Application
http://sites.google.com/site/leniel/blog/T4CodeGeneration.zip

Code output:

var usaStates = new List<State>
{
    new State()
    {
        Name = "Alabama",
        Abbreviation = "AL",
        Country = country2
    },
    new State()
    {
        Name = "Alaska",
        Abbreviation = "AK",
        Country = country2
    },
    new State()
    {
        Name = "Arizona",
        Abbreviation = "AZ",
        Country = country2
    },
    new State()
    {
        Name = "Arkansas",
        Abbreviation = "AR",
        Country = country2
    },
    new State()
    {
        Name = "California",
        Abbreviation = "CA",
        Country = country2
    },
    new State()
    {
        Name = "Colorado",
        Abbreviation = "CO",
        Country = country2
    },
    new State()
    {
        Name = "Connecticut",
        Abbreviation = "CT",
        Country = country2
    },
    new State()
    {
        Name = "Delaware",
        Abbreviation = "DE",
        Country = country2
    },
    new State()
    {
        Name = "Florida",
        Abbreviation = "FL",
        Country = country2
    },
    new State()
    {
        Name = "Georgia",
        Abbreviation = "GA",
        Country = country2
    },
    new State()
    {
        Name = "Hawaii",
        Abbreviation = "HI",
        Country = country2
    },
    new State()
    {
        Name = "Idaho",
        Abbreviation = "ID",
        Country = country2
    },
    new State()
    {
        Name = "Illinois",
        Abbreviation = "IL",
        Country = country2
    },
    new State()
    {
        Name = "Indiana",
        Abbreviation = "IN",
        Country = country2
    },
    new State()
    {
        Name = "Iowa",
        Abbreviation = "IA",
        Country = country2
    },
    new State()
    {
        Name = "Kansas",
        Abbreviation = "KS",
        Country = country2
    },
    new State()
    {
        Name = "Kentucky",
        Abbreviation = "KY",
        Country = country2
    },
    new State()
    {
        Name = "Louisiana",
        Abbreviation = "LA",
        Country = country2
    },
    new State()
    {
        Name = "Maine",
        Abbreviation = "ME",
        Country = country2
    },
    new State()
    {
        Name = "Maryland",
        Abbreviation = "MD",
        Country = country2
    },
    new State()
    {
        Name = "Massachusetts",
        Abbreviation = "MA",
        Country = country2
    },
    new State()
    {
        Name = "Michigan",
        Abbreviation = "MI",
        Country = country2
    },
    new State()
    {
        Name = "Minnesota",
        Abbreviation = "MN",
        Country = country2
    },
    new State()
    {
        Name = "Mississippi",
        Abbreviation = "MS",
        Country = country2
    },
    new State()
    {
        Name = "Missouri",
        Abbreviation = "MO",
        Country = country2
    },
    new State()
    {
        Name = "Montana",
        Abbreviation = "MT",
        Country = country2
    },
    new State()
    {
        Name = "Nebraska",
        Abbreviation = "NE",
        Country = country2
    },
    new State()
    {
        Name = "Nevada",
        Abbreviation = "NV",
        Country = country2
    },
    new State()
    {
        Name = "New Hampshire",
        Abbreviation = "NH",
        Country = country2
    },
    new State()
    {
        Name = "New Jersey",
        Abbreviation = "NJ",
        Country = country2
    },
    new State()
    {
        Name = "New Mexico",
        Abbreviation = "NM",
        Country = country2
    },
    new State()
    {
        Name = "New York",
        Abbreviation = "NY",
        Country = country2
    },
    new State()
    {
        Name = "North Carolina",
        Abbreviation = "NC",
        Country = country2
    },
    new State()
    {
        Name = "North Dakota",
        Abbreviation = "ND",
        Country = country2
    },
    new State()
    {
        Name = "Ohio",
        Abbreviation = "OH",
        Country = country2
    },
    new State()
    {
        Name = "Oklahoma",
        Abbreviation = "OK",
        Country = country2
    },
    new State()
    {
        Name = "Oregon",
        Abbreviation = "OR",
        Country = country2
    },
    new State()
    {
        Name = "Pennsylvania",
        Abbreviation = "PA",
        Country = country2
    },
    new State()
    {
        Name = "Rhode Island",
        Abbreviation = "RI",
        Country = country2
    },
    new State()
    {
        Name = "South Carolina",
        Abbreviation = "SC",
        Country = country2
    },
    new State()
    {
        Name = "South Dakota",
        Abbreviation = "SD",
        Country = country2
    },
    new State()
    {
        Name = "Tennessee",
        Abbreviation = "TN",
        Country = country2
    },
    new State()
    {
        Name = "Texas",
        Abbreviation = "TX",
        Country = country2
    },
    new State()
    {
        Name = "Utah",
        Abbreviation = "UT",
        Country = country2
    },
    new State()
    {
        Name = "Vermont",
        Abbreviation = "VT",
        Country = country2
    },
    new State()
    {
        Name = "Virginia",
        Abbreviation = "VA",
        Country = country2
    },
    new State()
    {
        Name = "Washington",
        Abbreviation = "WA",
        Country = country2
    },
    new State()
    {
        Name = "West Virginia",
        Abbreviation = "WV",
        Country = country2
    },
    new State()
    {
        Name = "Wisconsin",
        Abbreviation = "WI",
        Country = country2
    },
    new State()
    {
        Name = "Wyoming",
        Abbreviation = "WY",
        Country = country2
    },
};

usaStates.ForEach(s => context.States.Add(s));