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/

Productivity and happiness going from 4 to 8 GB RAM

This post tries to illustrate the productivity improvements you get when you go from 4 GB to 8 GB RAM.

I bought a Mac mini last year and when I started using it to do software development, data started to be processed very very slow… I ordered mini with “just” 4 GB RAM. My assumption that I’d develop only Mac software wasn’t true because now I know that I can’t live without Windows development. This way I have to use a virtual machine/VM (Windows 7) to install Visual Studio, database servers and everything else. That requires a lot of memory as you may know already.

Situation before and after memory upgrade

Mac OS Win VM Comment Feeling
Before 3 GB 1 GB This is crazy. I know. Necessity rules. Steaming mad
After 4 GB 4GB This is what a software developer needs today. Open-mouthed smile

There’s nothing better than images to illustrate my situation before and after the memory upgrade. Pictures can tell thousands words… Seeing is believing!

Before memory upgrade

Mac OS Activity Monitor  ( before memory upgrade ) Screenshot 1 - Mac OS Activity Monitor  ( before memory upgrade )

Parallels Desktop with Windows 7 virtual machine - Process Explorer ( before memory upgrade ) Screenshot 2 - Parallels Desktop with Windows 7 virtual machine
Process Explorer ( before memory upgrade )

Parallels Desktop with Windows 7 virtual machine - System Information ( before memory upgrade ) Screenshot 3 - Parallels Desktop with Windows 7 virtual machine
System Information ( before memory upgrade )

After memory upgrade

Mac OS Activity Monitor ( after memory upgrade ) Screenshot 4 - Mac OS Activity Monitor ( after memory upgrade )

Parallels Desktop with Windows 7 virtual machine - Process Explorer ( after memory upgrade ) Screenshot 5 - Parallels Desktop with Windows 7 virtual machine
Process Explorer ( after memory upgrade )

Parallels Desktop with Windows 7 virtual machine - System Information ( after memory upgrade ) Screenshot 6 - Parallels Desktop with Windows 7 virtual machine
System Information ( after memory upgrade )

Analysis and Points to take into account
1 - In both Activity Monitors (Screenshot 1 and Screenshot 4) you can see in the pie chart that the sum of memory (3.75 GB and 7.75 GB) is always 250 MB bellow the total installed memory. This is because Mac mini shares its RAM with the video card.

2 - I do not turn off the computer, that is, I put the computer to Sleep (Mac OS term for what we call Hibernate in Windows). When the computer comes back from sleep its memory isn’t completely empty. These screenshots were taken while the computer was in the process of sleeping/waking for a few days and hence the values of Wired, Active, Inactive memory tend to stay high even after waking the computer.

3 - The number of open apps is for sure different but I can guarantee that I have much more apps open after the upgrade, for example: two Visual Studios (desenv.exe) in Screenshot 5 while I had only 1 VS opened in Screenshot 2. To make it even more clear we just have to compare the Totals section of Win 7 System Information. Before I had 53 processes and after 75 (Screenshot 3 and Screenshot 6). An increase of 41.50%.

4 - Despite the values for Page outs and Swap used, after the upgrade I change screens very very fast. So these values do not scary me anymore.

5 - After upgrading Parallels Desktop 6 to its latest release (Build 6.0.12090 / Revision 660720; May 26, 2011) I noted a better memory management regarding the virtual machine. Now Page outs and Swap used are in MBs as shown in Screenshot 7:

Mac OS Activity Monitor after Parallels Desktop upgradeScreenshot 7 - Mac OS Activity Monitor after Parallels Desktop upgrade

Conclusion
Comparing the data shown above, it’s clear that RAM upgrade plays a big role when it comes to developer productivity and happiness.

Now I can say that I have a responsive computer and that means a lot in my day to day work.

Developer: get 8 GB RAM if you can afford it. It’s the bare minimum in 2011. You’ll be happy. No stress… If you can afford and your hardware supports 16 GB RAM, go for it and you’ll have no RAM headaches for a long long period.

Manager or whoever pays the bill: give a descent computer to your developers with at least 8 GB RAM! No excuses… RAM is so cheap nowadays that I don’t understand how can a lot of developers out there work with less than the necessary amount of RAM.

Note 1: Windows processes and memory information come from Process Explorer by Sysinternals. It’s way better and more powerful than Windows default Task Manager.

Note 2: To better understand memory information in Mac OS, refer to this article by Apple: Mac OS X: Reading system memory usage in Activity Monitor

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));

SVN, Hudson and MSBuild - Revision control repository

SVN, Hudson and MSBuild - Continuous Integration
SVN, Hudson and MSBuild - Building code on post commit

This three part series of posts I’m starting today serves as a guide on how to setup a continuous integration/CI server.

Tools I’m going to use (all FREE) and a brief description of them:

Continuous integration

Hudson Hudson

Hudson monitors executions of repeated jobs, such as building a software project or jobs. Hudson helps building/testing software projects continuously. In a nutshell, Hudson provides an easy-to-use so-called continuous integration system, making it easier for developers to integrate changes to the project, and making it easier for users to obtain a fresh build. The automated, continuous build increases the productivity.


Build automation

MSBuild (Visual Studio 2010 native build system)

The Microsoft Build Engine (MSBuild) is the build platform for Microsoft and Visual Studio. MSBuild is completely transparent with regards to how it processes and builds software, enabling developers to orchestrate and build products in build lab environments where Visual Studio is not installed.


Revision control

TortoiseSVN

TortoiseSVN is an easy-to-use SCM / source control software for Microsoft Windows and possibly the best standalone Apache™ Subversion® client there is. It is implemented as a Windows shell extension, which makes it integrate seamlessly into the Windows explorer. Since it's not an integration for a specific IDE you can use it with whatever development tools you like.

VisualSVN Server

VisualSVN Server allows you to easily install and manage a fully-functional Subversion server on the Windows platform. Thanks to its robustness, unbeatable usability and unique enterprise-grade features, VisualSVN Server is affordable both for small business and corporate users. It greatly simplifies installing and managing Subversion and is available for free.

The series will focus on continuous integration of .NET family of projects and I’ll write things in the order I installed them.

This 1st part will deal with the Revision Control repository configuration.

The only difference here is that I’m running all these software engineering tools on my Windows 7 virtual machine that is used exclusively for software development. A more ubiquitous situation is when/where you have each one of these tools working on their specific servers, that is, you’ll frequently see a dedicated continuous integration server, a dedicated build server and a dedicated repository server.

Having dedicated servers is good for a software development team/company. This definitely isn’t my case. I’m doing solo work and as such I decided to have a stable environment which can give me all the benefits of a continuous integration process running in a single machine (cost effective solution). This allows me to apply a better quality control over my code and at the same time I can automate common tasks.

These tools are working great on my machine right now where I’m developing and integrating an ASP.NET MVC 3 application. Bellow I’ll describe what you need in order to get a revision control repository running in your development machine.

I try to simplify every step going to the point and avoiding the intrinsic aspects such as why things are named or done this or that way.

When you finish this step by step guide, the following question should be answered:

Question: How/Why should I put my code under revision control?

Valid answer:

1 - Download and install TortoiseSVN. TortoiseSVN is a Subversion client.

2 - Create the SVN folder for your project. For example in C:\svn\MyProject. Right-click this folder and select TortoiseSVN → Create repository here. You should get a confirmation message like this one:

TortoiseSVN - Repository Successfully CreatedFigure 1 - Repository Successfully Created

3 - Create this temporary folder structure so that you have a standard SVN folder layout:

C:\Temp\MyProject\trunk
C:\Temp\MyProject\branches
C:\Temp\MyProject\tags

Do not mind the Temp folder for now. The main idea here is that you’ll put you current project’s folders/files inside the trunk folder. For more on this, read revision control.

4 - Move all MyProjects’s code to the trunk folder.

5 - Now right click the folder C:\Temp\MyProject and select TortoiseSVN → Import… Now you must provide the SVN address where you want to import your project. Do this:

TortoiseSVN - ImportFigure 2 - Import

Note the slashes ( / ) instead of back slashes ( \ ).

Before you import the project into the repository take note of:

- Just import the content needed to build the project. Remove unnecessary or temporary files/folders generated by the compiler e.g. Files = *.suo/*.user and folders = Bin/obj that contain compiled binaries.
- Organize your project in folders and subfolders. Using TortoiseSVN you can later rename/move files but it’s highly recommended that you set your project’s structure before importing!

After clicking OK, you should see this:

TortoiseSVN - MyProject Import FinishedFigure 3 - MyProject Import Finished

Great. Now we have just imported MyProject’s files to a revision control repository. See that we are at revision 1.

Now we’re going to check out this code in a different folder so that we can have the benefits of revision control.

6 - Create the folder C:\MyProject.

7 - Right click C:\MyProject and select SVN Checkout…

TortoiseSVN - CheckoutFigure 4 - Checkout

After clicking OK, you should get this:

TortoiseSVN Checkout FinishedFigure 5 - Checkout Finished

You’ll see that Windows Explorer now shows an icon in each file/folder informing that that specific file/folder is up to date or not with the one we have in the SVN repository:

Windows Explorer with Project Under Version ControlFigure 6 - Windows Explorer with Project Under Version Control

As you can see, I have changed the content of the file Main line Project Files and Folders go here.txt and as a result of this action I’m informed that this file was changed since the last check out (red exclamation mark). When I perform a commit to the repository this file that is marked as modified will be part of the head revision of the repository.

TortoiseSVN context menu options
SVN is a really powerful tool that has a lot of functions as the revision graph selected bellow:

TortoiseSVN - Context Menu OptionsFigure 7 - TortoiseSVN Context Menu Options

You’ll learn about and use each one of these functions for sure as the time passes by. Do not panic and take it easy! Surprised smile Be careful though when performing a commit to the repository and avoid breaking things. This can cause headaches.

Summary
In this simple step by step guide you saw how to put a project under revision control. This gives you a cool way of keeping track of what has been added, deleted, changed in your project since its start through what we call revisions. In addition you also get the chance of rolling back your changes in case anything goes wrong. This I think is the greatest feature of a revision control repository, that is, it acts in such a way that if I later decide to rollback to a previous version of a given file I’m allowed to do so because it keeps the old files. This works just like the database rollback function.

Next in this series
In the 2nd part of this series I’ll go into the details related to the installation and configuration of Hudson that is the continuous integration server.

More MP3 guessing pattern with Mp3tag in Mac OS

I mentioned in my last MP3 series post that I had the material to write a new post regarding this topic and so today I’m fulfilling the promise I made. I’m going to show how you can use the powerful Swiss Army knife called Mp3tag (Windows “only” app) right within your Mac. Yes, you read it right. You can run Mp3tag using your Mac. Nowadays almost everything is possible… Open-mouthed smile

The great thing here is that there’s a way to use our beloved Mp3tag in Mac OS. I even asked a question at SuperUser where I was trying to find some similar software that I could use in Mac. Here’s the question: Alternative to mp3tag for Mac OS X. Well, after searching a little bit more I found a great site called PlayOnMac. What is PlayOnMac?

PlayOnMac is like wineskin, winebottler or crossover a piece of software which allows you to easily install and use in your Mac numerous games and software designed to run only with Microsoft®'s Windows®.

I then found Mp3tag bundle here. You get a .dmg file which contains Mp3 Tag.app (172.5 MB) on disk. Just mount the .dmg file with a double click and extract the .app file to your Applications folder in Mac OS dock. Doing this you have the power of Mp3tag right inside your Mac. No Windows virtual machine needed/no need to load your bootcamp Windows. It’s good because you’ll have more memory left for your Mac (in the case of a virtual machine) or you won’t spend your time changing the operating system (in the bootcamp case).

Ok, the bundle is a little bit lazy to load and the visual (retro) is Windows like but who mind?

There’s nothing better than a use case to showcase something. Today I need to organize some MP3 tags because their Track - Artist - Title fields are all a complete mess. Some data are already there and some are missing completely. I of course will use Mp3tag guessing pattern feature to solve this.

To get to the Guess values dialog window, click the Actions (Quick) icon in the toolbar and select Guess values in the dropdown menu. Screenshots can be seen here.

Here are the MP3 files before the process:

Mp3tag Guess Values for Track Artist and Title in FileName [ before ]Figure 1 - Mp3tag Guess Values Window for Track Artist and Title in File Name [ before ]

As you see most of the tracks have 1 in the Track field and Title and Artist fields are missing in most of cases. All the information that is missing is present in the Filename field. Really interesting fact.

Let’s take advantage of Filename info and fill the missing tags with that data. How? Using Mp3tag Guess values window.

Source format: %_filename%

Guessing pattern: %track% - %artist% - %title%

Really easy, isn’t it?

What the hell these % symbols are doing here? They are simple placeholders that tell Mp3tag about a specific pattern found in fields of MP3 files. In this specific case, Mp3tag will extract data from the Filename field ( source ). Looking at Figure 1 we see that all file names follow a pattern: Track # - Artist - Title. Such pattern is then passed to Mp3tag in the Guessing pattern field. With this input Mp3tag is able to infer/parse the file names and split them accordingly. Each part will then be used to fill the correct MP3 tags.

Here’s the post-processed MP3 files:

Mp3tag Guess Values Window for Track Artist and Title in File Name [ after ] Figure 2 - Mp3tag Guess Values Window for Track Artist and Title in File Name [ after ]

That’s a much better view and experience within your preferred Media Library software!

Things like this makes me really happy with computers…

Hope you find it as useful as it’s being to me in this hard task of keeping a MP3 collection organized. I at least try my best.

To see more ways of using the Guessing Pattern, check the MP3 series of posts.

Access web app from Parallels Win 7 VM in Mac OS

About a month ago I started the creation of an ASP.NET MVC 3 app to a buddy. I’ll write some blog posts in the near future sharing my experiences with this awesome web framework. This app I’m still developing is a web application that I develop using my "new” computer. Today I use Mac OS as my main operating system and since I’m using ASP.NET I also need to use Windows.

Parallels Desktop is the way to go to have a Windows virtual machine [ VM ] running side by side with Mac OS. This way you have the best of both worlds. I discuss more about Parallels Desktop here.

One of the things I wanted to do during the development was seeing how the app would look like when viewed in a different OS, in this case, I’d like to see it running in Mac OS as if I were an end user accessing the app. This is good to test how the app behaves when it’s viewed in a different environment, that is, the perspective/environment of the end user, and not the developer’s perspective/development environment.

It’s cool to see the app running in a different OS and different web browsers. In Mac OS I have the chance of testing it against Safari too. I also have the chance of seeing how exceptions are being handled. There are certain types of errors that only happen when the end user is using the app in his environment. I think you know that already. This kind of testing allows me to deliver a better user experience.

When the app is accessed from the Mac side, the first difference is mainly visual because my Win 7 VM has all visual effects (clear type) disabled and so things look really simple if compared to Mac OS. I’m running the VM with no visual effects so that I have a somewhat fast VM to do the development. I only have 4 GB RAM available and I have to share it with both operating systems. I thought 4 GB RAM would suffice but that’s not the case. I have already ordered 8 GB RAM but am eagerly waiting it arrive from US. That’s another story that I plan to write in other blog post.

The reason I’m writing this post is that things don’t work at first when you try to access in Mac your app that is running in Windows VM. To get things working from both sides ( host = Mac OS and VM = Windows 7 ) they must see/communicate with each other. That’s where I had to do some work to get things going. So here are some instructions of what you need do:

1 - Set a fixed IP address for Windows VM
Set up a fixed IP address (read this page for a complete guide) in your Windows 7 virtual machine. This will make it easy when you need to access your app from Mac OS side. You won’t need to take note of different IP addresses handled by the DHCP server each time you restart your VM.

Windows 7 VM with fixed IP addressFigure 1 - Windows7 VM with fixed IP address

Above I have given the IP 192.168.1.106 to the VM. As I do not have a lot of devices connected to my wireless router, this IP is just fine.

2 - Set Virtual Machine’s Network Type
In your Parallels VM devices bar set your network type (orange arrow) as bridged:

Parallels Desktop VM devices toolbarFigure 2 - Parallels Desktop VM devices toolbar

3 - Test Communication between Host and VM
Make sure both operating systems can see each other.

Open Mac OS Terminal and execute a ping command:

ping 192.168.1.106

Change the IP with the IP you have in your Windows VM.

You should see something like this:

Mac OS Terminal pinging Windows 7 VMFigure 3 - Mac OS Terminal pinging Windows 7 VM

Now, open Windows 7 Command Prompt and ping Mac IP address. To see your Mac IP address go to System Preferences. Under Internet & Wireless, select Network.

Mac OS IP addressFigure 4 - Mac OS IP address

Let’s ping 192.168.1.100:

Windows 7 VM Command Prompt pinging MacFigure 5 - Windows 7 VM Command Prompt pinging Mac

As you see, both computers can see/communicate with each other. This means that the Network is working as expected.

4 - Configure Windows VM webserver for external traffic
What address should you type in a Mac OS browser to access the app? Well, this needs a little bit of caring. You’ll probably fail when trying to access the app with the same address you use in Windows side.

I’m using IIS Express as the web server in Windows. You can install it using Microsoft WebMatrix. IIS Express is by default configured to only server internal traffic (Windows VM). As I’m trying to access it from an external environment (Mac OS), I needed to configure it. The first link bellow was the main doc that helped me:

Serving external traffic with WebMatrix Beta
Handling URL Binding Failures in IIS Express (Serving External Traffic)

Basically I had to configure HTTP.SYS, create a URL binding that allow me to access the app in Mac side and disable Windows Firewall for my private home network.

4.1 - Configure HTTP.SYS
Type this command in Windows command prompt:

netsh http add urlacl url=http://leniel-pc:7777/ user=everyone

Change the url in the above command according to your desired configuration.

Note: the above command requires elevation. Right click the command prompt shortcut and select Run as administrator.

4.2 - Configure URL binding
As mentioned in the 2nd doc referenced above you must open your applicationhost.config file and add URL bindings that fit your needs. Mine is located here:

C:\Users\Leniel\Documents\IISExpress\config\applicationhost.config

Take a look at the last binding I have added:

<site name="FitnessCenter" id="3">

<application path="/" applicationPool="Clr4IntegratedAppPool">

<virtualDirectory path="/" physicalPath="C:\Users\Leniel\Documents\Visual Studio 2010\Projects\FitnessCenter\FitnessCenter.Web" />

</application>

<bindings>

<binding protocol="http" bindingInformation="*:7777:leniel-pc" />
<binding protocol="http" bindingInformation="*:7777:localhost" />

<binding protocol="http" bindingInformation=":7777:leniel-pc" />

<binding protocol="http" bindingInformation="192.168.1.106:7777:"/>

</bindings>

</site>

4.3 - Configure Windows VM firewall
The last step is disabling Windows VM firewall for your Home or work private networks.

Windows 7 VM FirewallFigure 6 - Windows 7 VM Firewall

As I just use this virtual machine to do software development, it’s ok to me to disable the firewall. Be careful though. Surprised smile

5 - Access Win 7 VM web app in Mac OS
After all this configuration you should be able to browse your app externally.

To see if everything is working, open a browser in Mac OS and try to access the app typing something as this in my case:

http://192.168.1.106:7777/

That’s it.

Hope it helps.

Batch renaming and copying MP3 values from tag to tag

This is just one more post in the MP3 series where I write about how you can organize your MP3 library/collection performing batch renaming to correct MP3 metadata and save time.

Just after I posted about Using Regular Expressions to correct mistagged MP3 I had other task to accomplish with Mp3tag but I didn’t know how to do that. What I want now is somewhat like what I showed in that previous post. The difference is that now I want to use the content of a given tag to fill another tag, thus copying/cutting that content from tag to tag.

As always I have a feeling that there must be a way of doing what I want and as always I go after it.

Here’s the problem this time:

Title tag has both the Title and Artist names and Artist tag has a wrong valuePicture 1 - Title tag has both the Title and Artist names and Artist tag has a wrong value

As you see in Picture 1, I have some MP3 files that have the Artist name included in the Title tag. The Artist name is between brackets while the Artist tag has the text Various. Obviously this is wrong because the Artist name should be in its proper Artist tag.

How can we correct this without manual intervention that would be a time consuming task?
The simple answer is using Mp3tag and its powerful set of features like the one called Guess values.

Guess values is so amazing that its window dialog just takes 2 parameters to get the job done. They are: Source format and Guessing pattern.

The Source format field in this case has the value %title% and the Guessing pattern field has the value %title% (%artist%). You can use any mp3 tag field between percent signs (%). What do these values mean?

Source format retrieves the value present in the Title tag of each MP3 and by guessing the values through the Guessing pattern it’s possible to split the Title tag according to the pattern. This opens up a world of possibilities where you can use any kind of combinations (masks/patterns) to split the value of a given tag and have those split values fill any MP3 tag of your choice.

So, in this case we have something as this happening behind the curtains:

Title: Begins With Me (Point of Grace)

Source format = %title%
Guessing pattern = %title% (%artist%)

Making the substitution, we have:

Guessing pattern = %Begins With Me% (%Point of Grace%)

With this, Mp3tag now knows what values it must use to fill the MP3 tags we want correctly.

Here’s the final result after you press the OK button present in Picture 1:

Correctly tagged MP3 files
Picture 2 - Correctly tagged MP3 files

Hope you got the idea and make good use of this.

I already have the material to write the next post in this series. Keep an eye in this blog. :)