Chris Surfleet's MentalReboot

Code insights tinged with a spot of insanity

Continuing with the general theme of Func<>, Action<> and generics here is a nice little trick to replace long but simple switch statements. Often in your code you will find statements like this:

switch (category)
{
  case Category.Articles:
    DisplayBox.Text = "There are " + GetArticleCount(user) + " articles";
    break;
  case Category.Posts:
    DisplayBox.Text = "There are " + GetPostCount(user) + " posts";
    break;
  case Category.Comments:
    DisplayBox.Text = "There is " + GetCommentCount(user) +
             " comments on " + GetPostCount(user) + " posts";
    break;
  case Category.Votes:
    DisplayBox.Text = "Found " + GetVoteCount(user) + " recent votes";
    break;
  case Category.Videos:
    DisplayBox.Text = "Found " + GetVideoCount(user) + " submitted videos";
    break;
}

It’s pretty ‘wordy’ for a start, and crucially we are duplicating the DisplayBox.Text = bit 5 times which is not ideal. Wouldn’t it be nice to make the code shorter, clearer and better? It is really almost too easy! Define a new typed dictionary with Category as a key and a Func<> as the value:

var categoryMap = new Dictionary<Category, Func<User, string>>
{
  { Category.Articles, u => "There are " + GetArticleCount(u) + " articles"},
  { Category.Posts, u => "There are " + GetPostCount(u) + " posts" },
  { Category.Comments, u => "There is " + GetCommentCount(u) +
                      " comments on " + GetPostCount(u) + " posts" },
  { Category.Votes, u => "Found " + GetVoteCount(u) + " recent votes" },
  { Category.Videos, u => "Found " + GetVideoCount(u) + " submitted videos" }
};

The Func<User, string> declaration defines an inline method taking a single User argument and returning a string. All we then need to do is check we have a matching Category key in the map and then execute the matching function:

if (categoryMap.ContainsKey(category))
  DisplayBox.Text = categoryMap[category](user);

This is overall more than a third less lines, and your intent is more clearly described.

Update: Dave Van den Eynde pointed out the issue of the overhead involved setting up a dictionary. I ran some tests, which you can read about in the comments, the result being there is a slight overhead using this technique (0.003ish of a millisecond) when run a single time. If this code exists in a loop however, define the dictionary outside of your loop and it can actually be very slightly faster (1 millisecond in 10,000 repetitions) than using switch. I thought it was worth pointing out the pros and cons here for those who don't read comments.

Happy coding!

Did you find this post useful or interesting? I'd be really grateful if you could return the favour by clicking this google link:



If you’re waiting for part 2 of the TFS FTP stuff don’t worry its coming, I’m just waiting on an install by our tech guys so I can finish testing on it.

Following on from the little extension method I posted yesterday which used Action<T> I started thinking of the many uses I’ve found for Action and Func. Cue a mini-series of posts on easy tricks you can do with them! First up – using generics and Func to reduce endless lines of viewstate and session code.

Firstly, if you are using code like this:

public void DoSomething()
{
  DateTime lastActivity = DateTime.Now;

  if (Session["LastActivity"] != null)
    lastActivity = (DateTime) Session["LastActivlity"];

  MyLabel.Text = "Last used: " + lastActivity.ToShortDateString();
}

You are doing it wrong! Littering your code with calls to session is not only asking for runtime errors (noticed the bug in the code above?) but makes it really awkward to swap out your mechanism – if you wanted to switch to SQL server persistence say, you would have to change every single reference - urgh!

So wrap it in a property, which is sat somewhere all code which requires it can see it, maybe a base Page class or your masterpage:

public DateTime LastActivity
{
  get
  {
    if (Session["LastActivity"] == null)
      Session["LastActivity"] = DateTime.Now;
    return (DateTime) Session["LastActivity"];
  }
  set { Session["LastActivity"] = value; }
}

public void DoSomething()
{
  MyLabel.Text = "Last used: " + LastActivity.ToShortDateString();
}

Nice, its all type-safe and your DoSomething code is clearer in intent. You will find though that you soon end up with loads of these properties defined in a row - I designed this helper after opening a file with 30 of them in it! I’ve therefore designed a little bit of code to simplify defining the property:

public T GetSession<T>(string item, Func<T> defaultValue)
{
  if (Session[item] == null)
    Session[item] = defaultValue();
  return (T)Session[item];
}

You can then define your property like this:

public DateTime LastActivity
{
  get { return GetSession("LastActivity", () => DateTime.Now); }
  set { Session["LastActivity"] = value; }
}

How does it work? Well you could have written the call as GetSession<DateTime>(“LastActivity”, () => DateTime.Now) which will make things clearer. We’re using the generic type T – in this case a DateTime and checking to see if the Session contains one. If not we set the session to the result of a function which returns a DateTime. We didn’t define the DateTime type in our above code because the framework is smart enough to figure it out from the return type of the Func argument.

You might be wondering why we didn’t just define GetSession as T GetSession<T>(string item, T defaultValue). The benefit of using Func is that it will only get run when the session is empty. If we were to just pass T, that would be populated into memory every time you called your property, this is not much of an issue here, but would be a huge performance drain if you were accessing a datastore or the like:

return GetSession(“LastActivity”, SqlDatabase.Users(CurrentUser.Name).LastActivity)

You can also of course do exactly the same with the ViewState:

protected T GetViewState<T>(string item, Func<T> defaultValue)
{
  if (ViewState[item] == null)
    ViewState[item] = defaultValue();
  return (T)ViewState[item];
}
Happy coding!

Did you find this post useful or interesting? I'd be really grateful if you could return the favour by clicking this google link:



Just a quick post to show a little extension method I created. I’m not quite sure why it’s not part of the framework already really! I'm fast beginning to see the foreach loop as a code smell, preferring a more declarative approach. In the olden-days (before-linq) it would be common to write code like this:

foreach (Person person in People)
{
  if (person.LastLogin > DateTime.Now.AddDays(-10))
    person.RecentlyActive = true;
}

Then along came linq and it became:

foreach (Person person in People.Where(p => p.LastLogin > DateTime.Now.AddDays(-10)))
{
  person.RecentlyActive = true;
}

Its getting fairly declarative now but we’re still using the loop and wasting space. You can improve this though by defining an extension method like this:

public static void Do<T>(this IEnumerable<T> items, Action<T> modifier)
{
  foreach (T item in items)
    modifier(item);
}

It just loops through each of the items in a collection and performs some action based on them. We can now change our person code to:

People.Where(p => p.LastLogin > DateTime.Now.AddDays(-10)).
    Do(p => p.RecentlyActive = true);

Nice huh?

Is there a reason this wasn't included in the framework? Is it there and I missed it? Am I doing something horrible? Will I burn eternally for this? Let me know Wink

Did you find this post useful or interesting? I'd be really grateful if you could return the favour by clicking this google link:



Over the last couple of years, we’ve been working slowly through the Continuous Deployment evolution; we started continuously integrating, and then began continuously deploying to test, and then to staging. It’s been hugely liberating but the inevitable next step was pretty daunting: can we get any closer to continuous deployments to live? Putting aside the ideal of a deployment on each checkin for a now I decided the first step should be getting a TFS build which can do the deployment.

This is the first in a 3-part series in which I will go through a complete FTP deployment solution, which will be able to push any configuration of your web application to a remote server and will be fully configurable from a TFS Build Definition. For this first part I will go over how to build the FTP component, while the later posts will cover first a custom TFS workflow and then some custom configuration management.

Why a Team Build Activity?

There are FTP tasks available in MsBuild, so it would be totally possible to get your application FTP’d from your build file. Why not? Well I think that we should have a separation of concerns here, your csproj file handles building and constructing your product, while Team Build handles deployment. This then allows you to control all your deployment setup in each Build Definition.

(A side note here, this leaves the question of where you handle configuration such as web.config transformation, personally I do this from Team Build but it’s a grey area.)

If you can’t be bothered working through the tutorial (shame on you!) you can get the finished product here: FtpDeployment.zip (57.53 kb)

WinSCP

This solution makes use of the WinSCP utility, so you will need to install it first on your build server and dev boxes. This install includes a COM object we can send our FTP commands to.

Project Setup

OK, create yourself a new class library project and add references to the following assemblies, and ensure each has Copy Local set to false:

  • Microsoft.Build.Framework
  • Microsoft.Build.Utilities.v4.0
  • Microsoft.TeamFoundation
  • Microsoft.TeamFoundation.Build.Client
  • Microsoft.TeamFoundation.Build.Workflow (In C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies\)
  • Microsoft.TeamFoundation.TestImpact.BuildIntegration (Also in PrivateAssemblies)
  • Microsoft.TeamFoundation.TestImpact.Client (In C:\Windows\assembly\GAC_MSIL\
    Microsoft.TeamFoundation.TestImpact.Client\10.0.0.0__b03f5f7f11d50a3a\)
  • Microsoft.TeamFoundation.VersionControl.Client
  • Microsoft.TeamFoundation.VersionControl.Common
  • Microsoft.TeamFoundation.WorkItemTracking.Client
  • System.Activities
  • System.Activities.Presentation

FTP Code

Now create a new class called FtpSynchronizer to hold the code we will use to perform the FTP. Add the following method to it:

public bool PublishWebsite(string ftpSite, string ftpUserName,
            string ftpPassword, string ftpFolderPath, string localFolder,
            string winScpLocation = @"C:\Program Files\WinSCP\winscp.com")
{
  using (Process winScp = new Process())
  {
    winScp.StartInfo.FileName = winScpLocation;
    winScp.StartInfo.RedirectStandardInput = true;
    winScp.StartInfo.UseShellExecute = false;
    winScp.StartInfo.CreateNoWindow = true;
    winScp.Start();

    winScp.StandardInput.WriteLine("option batch abort");
    winScp.StandardInput.WriteLine("option confirm off");
    winScp.StandardInput.WriteLine("open ftp://" + ftpUserName + ":" +
                  ftpPassword + "@" + ftpSite);
    winScp.StandardInput.WriteLine("synchronize remote " + localFolder +
                  " /" + ftpFolderPath);
    winScp.StandardInput.WriteLine("exit");

    winScp.StandardInput.Close();

    winScp.WaitForExit();

    return (winScp.ExitCode == 0);
  }
}

The first block is firing up WinSCP in the background and preparing it for commands, it’s the second block we are more interested in. This simply opens up a batch-capable connection to an FTP site and sends a “synchronize remote” command. This will copy any newer files from the local folder to the FTP server. It will not delete files on the server which are not on the client, this is the safer behaviour but could be a gotcha in certain circumstances.

After firing the commands we wait for WinSCP to do its stuff, and then return true if everything was successful, or false if not. This isn’t ideal, and I’ll post soon on how to add better error handling to this component, but it is workable in a basic scenario.

At this point, test your class. Go set yourself up a unit test project and create some tests against your FTP site. Done? Brill!

App_offline.htm

This code works, but it would be nice if users of the application weren’t shown an ugly 404 page if they try to access the site while its being updated. Lets use an app_offline file!

In the FtpSynchronize class add the following at the beginning of the PublishWebsite method:

string appOfflinePath = Path.Combine(Path.GetTempPath(), "app_offline.htm");

using (StreamWriter stream = File.CreateText(appOfflinePath))
{
  stream.Write("<html><body><h1>Application currently undergoing maintenance, " +
      "check back in a few minutes.</h1></body></html>");
  stream.Close();
}

 

Obviously you could make the page contents nicer if you like. Then you simply need to upload it before your synchronization, and remove it after, so surround the “synchronize remote” line like this:

winScp.StandardInput.WriteLine("put " + appOfflinePath);
winScp.StandardInput.WriteLine("synchronize remote " + localFolder +
                     " /" + ftpFolderPath);
winScp.StandardInput.WriteLine("rm app_offline.htm");

Done!

Creating the TFS CodeActivity

We’ve got a nice little FTP synchronizer now, but we need to be able to access it from Team Build. For this we’ll create a new class called FtpSynchronizeActivity:

 

[BuildActivity(HostEnvironmentOption.All)]
[BuildExtension(HostEnvironmentOption.All)]
public sealed class FtpSynchronizeActivity : CodeActivity<bool>
{
  protected override bool Execute(CodeActivityContext context)
  {
    throw new NotImplementedException();
  }
}

This will be an activity we can use in a TFS workflow which will return success or failure. (If that made no sense don’t worry, we’ll get to it shortly) We’re going to need some inputs to pass the required info to the FtpSynchronizer so add these above the Execute method:

public InArgument<string> WinScpComLocation { get; set; }

[RequiredArgument]
public InArgument<string> LocalFolder { get; set; }

[RequiredArgument]
public InArgument<string> FtpSite { get; set; }

[RequiredArgument]
public InArgument<string> FtpUserName { get; set; }

[RequiredArgument]
public InArgument<string> FtpPassword { get; set; }

public InArgument<string> FtpFolderPath { get; set; }

These values will be available as properties of the activity in our workflow. All we need to do now is link up the Execute method to the FtpSynchronizer:

protected override bool Execute(CodeActivityContext context)
{
  string winScpLocaton = context.GetValue(WinScpComLocation);
  string localFolder = context.GetValue(LocalFolder);
  string ftpSite = context.GetValue(FtpSite);
  string ftpUserName = context.GetValue(FtpUserName);
  string ftpPassword = context.GetValue(FtpPassword);
  string ftpFolderPath = context.GetValue(FtpFolderPath);

  if (ftpFolderPath == null)
    ftpFolderPath = String.Empty;

  FtpSynchronizer synchronizer = new FtpSynchronizer();
  return String.IsNullOrWhiteSpace(winScpLocaton) ?
    synchronizer.PublishWebsite(ftpSite, ftpUserName, ftpPassword,
                    ftpFolderPath, localFolder) :
    synchronizer.PublishWebsite(ftpSite, ftpUserName, ftpPassword,
                    ftpFolderPath, localFolder, winScpLocaton);
}

Next Steps

All that remains to be done is include the activity in a Team Build workflow. If you've not done this before I'll be covering it in part 2 coming up in a few days. In part 3 we will overcome a couple of problems with configuration and add a bit of extra functionality to the deployment workflow. Watch this space!

Full code for this example is available here: FtpDeployment.zip (57.53 kb)

Did you find this post useful or interesting? I'd be really grateful if you could return the favour by clicking this google link:



Yes yes, I know this is a technology blog, and I promise not to make a habit of pushing charity stuff at you when you just want to know about build systems etc. That said, this particular issue has really had an effect on me, and I would very much like to spread the word.

I do a lot of rock climbing in my (limited) spare time and it has had an overwhelmingly positive effect on my life, one which I think should be shared with and available to everyone. This is a view shared by Samuel Farmer, a black man who grew up in a poor area of Liverpool and later in life became an ourdoors instructor. He has devoted his life to giving disadvantaged children from areas such as those he grew up in the chance to get out into the countryside, challenge themselves and expand their horizons. To this end he set up 'The Hope Project', a charity based in Cornwall, UK.

Unfortunately, over the last few years Sam has been on the receiving end of a stream of racist abuse from a minority of people in his local area who view outsiders – particularly young black outsiders – as unwelcome. This came to a head in January when his outdoors centre was burnt to the ground, causing £70,000 worth of damage. Shockingly, the local police and council do not want to know, and have been almost no real investigations into the arson, despite coverage by news media.

runined

At the beginning of this month, a thread started on the UK’s largest climbing forum and spawned a wonderful grassroots help project for Sam. He has received many parcels of free gear from individuals across the UK to help him get back on his feet. What he really needs now though is money with which to start rebuilding so that he can begin once again helping those with less opportunity in life.

You can read more about his ordeal here: http://tohatchacrow.blogspot.com/2012/02/black-outdoor-instructor-targeted-by.html

The UKClimbing thread is here: http://www.ukclimbing.com/forums/t.php?n=493772

If you are reading this the chances are that you earn a decent wage, and enjoy a pretty good life. Please visit the Hope Project's website and donate just a fraction of what you could and you will be helping not just Sam, but any number of children he will then be able to help on your behalf. I am donating, and I am also pledging the entire advertising revenue of this blog in 2012 to his cause.

You can donate here: http://www.thebeaconofhope.co.uk/

Thanks so much

Chris Surfleet

Did you find this post useful or interesting? I'd be really grateful if you could return the favour by clicking this google link:



Dec
28

Year End Roundup

by Chris | Tags:

Well here we are; 2011 is drawing to a close and every blog seems to have an end of year review, who am I to buck the trend? So here are the things of note from my 2011, going from vaguely technical to totally personal:

The Wonders Of Agile

The members of our development team in my day job have been pushing agile for a while now, but it’s only been during 2011 that I think we got the process down and finally started to get the buy-in from outside the team that you really need to make it work. The benefits have been obvious and we won’t be going back any time soon!

Get in the SCRUM

Continuous Deployment

This has been central to the success of the agile process for us. While we’re nowhere near the holy grail of continual deployment to live, it cannot be overstated the difference that continual deployment to test has made. If a TFS user story is marked as completed the product owner knows it is available to them, which saves plenty of problems with forgotten deployments causing a lack of confidence from the product owners. This is made possible by the power of TFS build and it takes a matter of moments to set up for new projects.

Setting up continuous deployment in TFS

WiX

If you’ve been following the blog you already know I’m a bit into WiX! With our current work environment we have no control over staging or live servers and so creating proper installers has been a total lifesaver. We can now be confident that what we want on the server will be what actually ends up there!

My series on WiX

Irony

Irony is an open source .net language implementation kit. That is, it allows you to create your own custom languages and parse them at run time. That in itself is nothing new, but Irony is the best I’ve used to date. Performance is good enough to make it useful in real-time processing applications too! If you want to allow custom modification of your application’s behaviour it’s the way to go (IMHO). Documentation is a bit thin on the ground so I’ll be running a series of posts on it in the New Year.

Irony on CodePlex

Blogging

This blog has only been running since July but I’ve had a great time writing every post. I thought I’d make my stats public for you:

  • 12 posts;
  • Averaging around 50 visits a day on weekdays, steadily rising month on month;
  • 8000 visits in total;
  • Just under 2000 unique visitor, thanks for coming back!
  • The vast majority of search traffic comes in through Google. But you knew that already;
  • The top two referring keywords were 'wix' and 'IIS'.

Kindle

I’ll admit it, I scorned the kindle. I trotted out all the familiar excuses: ‘Its just not the same’, ‘where’s the book smell?’ etc. The truth is though that this was my best purchase of the year. Since buying it I have been reading more, and reading more often. It lives in my jacket pocket so it’s always there to help my fill a spare ten minutes. I love being able to get the next book in a series instantly when I finish the one before. The amount of books for under £2 is brilliant. Get one. Now.

Amazon kindle

So, there is my 2011. I’ll be back in the New Year with a couple more deployment and WiX posts and I’ll get started on a new Irony series. I hope you have all had a great festive season and that you enjoy your New Year!

Did you find this post useful or interesting? I'd be really grateful if you could return the favour by clicking this google link:



Hey, it’s been a while! The usual #lifegetsintheway excuses all apply, but you knew that right? Fear not though, treasured reader, for today we are going to delve into the adrenaline-filled world of MsBuild/TFS interaction!

But Why?

There are plenty of reasons for wanting to interact with your TFS server from MsBuild. Maybe you want to ensure that you always get the latest version of a referenced DLL on each build? You could need to update a source-controlled log file when a certain configuration is built on your continuous integration machine? All of these sorts of things are possible and I’ll be going over some of the more complex scenarios in future articles. For now though I want to fix a bit of an annoyance with the web or service installers I went through in previous posts. If you haven’t worked through these don’t worry, you can get the finished source for the service installer here: WixServiceInstallerExample.zip (184.90kb). (You will need WiX installed, but that is a good thing)

Lets Go

Lets start by recreating the problem; step one is to add the service installer solution to source control and check everything in. Once done, try to build the installer project. Oh no!

Unable to execute transformation. Access to the path ‘……..’ is denied.

What is happening here? The installer build process needs write access to the file WixServiceInstallerExample.wxs, which is denied because it is currently checked in. You can verify this by checking the file out and building again, which will work. So all we need to do is make this automatic.

Open up the project file for the installer (right click > Unload project, right click > Edit WixServiceInstaller.wixproj). Add the following three properties to the top PropertyGroup element:

<TfsExeLocation>$(Vs100ComnTools)..\IDE\tf.exe</TfsExeLocation>
<CheckinComment>Checkin for installer build.</CheckinComment>
<CheckinOverrideReason>Checkin for new installer build.</CheckinOverrideReason>

The first item is the path to the ft.exe file on your computer. Unfortunately we have to do all of this stuff using the command line! The nice thing about defining it here though is that if you have a wacky install on your build box like I had to deal with for one client, causing the TFS build to break, you can override this property in your TFS build definition.

The other two items are there for when we check the file back in again, ensuring we don’t get any policy blocks on checkins.

Now, right at the beginning of the BeforeBuild target at the bottom of the file, add this line:

<Exec Command=""$(TfsExeLocation)" checkout %(ProjectReference.Filename).wxs"
               Condition="'%(ProjectReference.ContentProject)'=='True'" />

The Exec command is deliciously powerful; suddenly the power of any command-line tool you can find or build is at your beck and call. There really isn’t a huge amount to this; we pass a ‘checkout’ command and the path to the wxs file to the ef.exe command. Next add the following right at the end of the BeforeBuild target:

<Exec Command=""$(TfsExeLocation)" checkin %(ProjectReference.Filename).wxs
                 /comment:"$(CheckinComment)" /noprompt
                 /override:"$(CheckinOverrideReason)""
        Condition="'%(ProjectReference.ContentProject)'=='True'" />

This time we’re checking the file in. It’s a pretty similar command to the last one but we are just passing a few more arguments specifying comments and policy override reasons to be stored next to the checkin. The noprompt option tells tf.exe not to pop up any dialogs etc.

Check out the show!

That was easy huh? You can go ahead and reload the project now. Make sure all files are checked in and run the build. Keep watching the solution explorer window and you should see the wxs file’s icon changing to checked out, and back to checked in as the build progresses. Happy days.

Limitations

Just bear in mind that this code is always run when you build. That means that even if the wxs file is checked out before you build, it will end up checked in afterwards.

Hopefully this post will have been of use to someone, but I’m off to get started on the Christmas celebrations. Merry Christmas to those of you who celebrate it!

Did you find this post useful or interesting? I'd be really grateful if you could return the favour by clicking this google link:



A quick one today guys, it’s Friday and I’m sure you have beer to drink, so we’ll keep this short!

In my last post on creating WiX windows service installers you might have noticed a glaring omission. If you try to install the service on a box where you don’t have enough permissions to install or start a service then the install will bomb out near the end. It doesn’t cause any real problems, but it would be nicer if it warned you right at the start instead of letting you through all the screens first.

Fear not though, for it is a really easy change. There is a WiX element called Condition, which has its condition statement as its text, and a Message attribute for the message to display on failure. Here are a few examples of things you can do, although there are a loads of other possibilities! These can be placed right after the Package node in your Product.wxs file.

Require Admin Rights

<Property Id="MSIUSEREALADMINDETECTION" Value="1" />
<Condition
  Message="This installer can only be used by admin users."
  >Installed OR (Privileged AND AdminUser)</Condition>

Require .Net 4.0

<PropertyRef Id="NETFRAMEWORK40FULL"/>
<Condition
  Message="This application requires .net version 4.0 or higher."
  >Installed OR NETFRAMEWORK40FULL</Condition>

Require IIS 7

<PropertyRef Id="IISMAJORVERSION"/>
<Condition
  Message="This application requires IIS 7 or higher."
  >IISMAJORVERSION >= "#7"</Condition>

 

And that's it! Try it out.

Did you find this post useful or interesting? I'd be really grateful if you could return the favour by clicking this google link:



Those of you who’ve been following my blog lately will know that I’ve been writing a lot on the subject of WiX installers. Basically I just think they’re awesome! The previous set of posts focused on the basics and then some of the specific stuff you can do with website installers. This time I’m going to walk through the creation of a service installer, and introduce you to a simple technique that I’ve found loads of uses for.

I’m assuming you already know the basics, if you’ve never touched WiX and don’t know about running the Heat command from your project file take a look at the first two posts on the subject.

What Do We Want From This Installer?

A good installer will take all of the complications out of installing a product, and this one should be no different. Our installer should:

  • Install to any given directory;
  • Start automatically;
  • Pull the correct configuration information in with it.

Not a long list, so this shouldn't take long!

Creating The Service

Let’s start by creating a new windows service, mine’s called WixServiceInstallerExample but you could call yours something more boring if you like. In the Service1 code-behind remove the two overridden methods; we don’t actually need our service to do anything for this example!

Creating The Installer

OK, now that the service is created, let’s get on with the installer. Add a new WiX Setup project to the solution. You need to add a reference to your service project first, then references to the WixNetFxExtension and WixUIExtension dll files from the WiX install folder.

OK, now we’ll make a couple of changes to the Product.wxs file. For now we’re just ripping out a bit of the stuff we don’t need and telling WiX to use the WixUI_InstallDir standard user interface. Replace the Directory and Feature nodes with the following:

<Directory Id="TARGETDIR" Name="SourceDir">
  <Directory Id="INSTALLLOCATION" Name="WixServiceInstaller">
  </Directory>
</Directory>

<Feature Id="ProductFeature" Title="WixServiceInstaller" Level="1">
  <ComponentGroupRef Id="WixServiceInstallerExample_Project" />
</Feature>

<Property Id="WIXUI_INSTALLDIR">INSTALLLOCATION</Property>
<UIRef Id="WixUI_InstallDir" />

We’ll be back in here later to do some cool service-oriented stuff, but for now you can close the file.

Next we’ll do something fairly similar to what we did with heat in a previous post, with a couple of funky changes. Unload the installer project and edit the project file.  Locate the ProjectReference node which points to your referenced project and add the following as its last child:

<ContentProject>True</ContentProject>

We’ll use this to ensure we don’t end up running heat on every reference. Now add a new BeforeBuild target right before your closing </Project> tag:

<Target Name="BeforeBuild">
  <MSBuild Projects="%(ProjectReference.FullPath)" Targets="Build"
    Properties="Configuration=$(Configuration);Platform=AnyCPU"
    Condition="'%(ProjectReference.ContentProject)'=='True'" />
  <PropertyGroup>
    <LinkerBaseInputPaths>%(ProjectReference.RootDir)%(ProjectReference.Directory)bin\$(Configuration)\</LinkerBaseInputPaths>
  </PropertyGroup>
  <HeatDirectory OutputFile="%(ProjectReference.Filename)-temp.xml"
    Directory="%(ProjectReference.RootDir)%(ProjectReference.Directory)bin\$(Configuration)\"
    DirectoryRefId="INSTALLLOCATION"
    ComponentGroupName="%(ProjectReference.Filename)_Project"
    SuppressCom="true"
    SuppressFragments="true"
    SuppressRegistry="true"
    SuppressRootDirectory="true"
    AutoGenerateGuids="false"
    GenerateGuidsNow="true"
    ToolPath="$(WixToolPath)"
    Condition="'%(ProjectReference.ContentProject)'=='True'" />
  <XslTransformation
    XmlInputPaths="%(ProjectReference.Filename)-temp.xml"
    XslInputPath="XslTransform.xslt"
    OutputPaths="%(ProjectReference.Filename).wxs"
    Condition="'%(ProjectReference.ContentProject)'=='True'" />
  </Target>

So what’s going on here? Firstly we build the required project to make sure it’s available for heat. Then we run heat on the built content just like we would with a published web project. Notice though that we’re outputting to a temp.xml file. This is because the xml file definitions created by heat aren’t quite what we need in this case. Instead we output to the temporary file and then in the final step we perform an xsl transformation, saving the final file as WixServiceInstallerExample.wxs to be used by the installer.

You’re probably wondering what we need to do this little transformation for. The problem we have is that we need to do some specific service oriented stuff with the actual WixServiceInstallerExample.exe file in our Product file. If we define one file in two places WiX will have a fit, so to keep the peace we’ll remove it from the auto generated file. We’re done with the project file so close it and reload the project in visual studio. Add a new XslTransform.xslt file to your project (You can do this by adding a text file but just giving it an xslt extension).

 

Add the following to the file:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0"
  xmlns:w="http://schemas.microsoft.com/wix/2006/wi">

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="w:Component[w:File/@Source='SourceDir\WixServiceInstallerExample.exe']">
    <w:Component>
      <xsl:attribute name="Id">
        <xsl:value-of select="@Id"/>
      </xsl:attribute>
      <xsl:attribute name="Guid">
        <xsl:value-of select="@Guid"/>
      </xsl:attribute>
      <xsl:attribute name="KeyPath">yes</xsl:attribute>
    </w:Component>
  </xsl:template>

</xsl:stylesheet>

It’s not really too complicated. We duplicate everything in the source file except for the exe file, which is removed. Awesome, we’re nearly there now! Build your project and you should get an error about an unresolved reference to WixServiceInstallerExample_Project. This is because it has been created but not yet added to the project. Click to view hidden files and include the file WixServiceInstallerExample.wxs in your project.

If you take a look in this file you’ll notice that the first Component doesn’t have a File in it – this is where we stripped out the main exe.

You should be able to build your project now, but it’s still not installing any service specific stuff. Open up your Product.wxs file and add the following inside the INSTALLLOCATION Directory node:

<Component Id="ServiceComponent" Guid="YOUR_GUID_HERE">
  <CreateFolder />
  <File Id="MainExecutable" KeyPath="yes"
    Source="SourceDir\WixServiceInstallerExample.exe" />
  <ServiceInstall Id="ServiceInstaller"
    Type="ownProcess"
    Vital="yes"
    Name="WixServiceInstallerExample"
    DisplayName="Wix Service Installer Example"
    Description="An example service."
    Start="auto"
    Account="LocalSystem"
    ErrorControl="ignore"
    Interactive="no" />
  <ServiceControl Id="StartService"
    Start="install"
    Stop="both"
    Remove="uninstall"
    Name="WixServiceInstallerExample"
    Wait="yes" />
</Component>

It’s that easy. We specify the file we stripped out earlier and then first install and then start the service. All that’s left is to add a reference to the component into your Feature section:

<ComponentRef Id="ServiceComponent" />

Testing

We’re done! Go ahead and build your project and then run the resulting msi file. If you watch the statuses as they flash past you’ll see that the files are copied and then the service is installed and started. Take a look at your services list and it should be visible and started!

Why This Way?

The technique I’ve shown you here of using xslt files for modifying heat output has many more uses than the one outlined above, such as applying permissions on certain files. I’ll do a quick post on how to do that sometime soon.

Adding App.config Transformations

One final little thing; if you have read my post on App.config transformations you’ll know that my favoured way of applying them is during an WiX installer build. This is actually really simple:

  1. Firstly set up the transformations in your service project;
  2. Next unload your installer project and edit the project file;
  3. In the BeforeBuild section, change the MsBuild node so that its Targets attribute is set to TransformAppConfig (or whatever you called your target in the service project);
  4. There is no step 4!

Finished

I hope this post has been useful to somebody. I’ll be following up soon with a couple of posts on using xslt to add file permissions, and how to put pre-requisites on an installer (try running your installer somewhere you don’t have local admin permissions, or somewhere with an old version of the .net framework!) so you should subscribe to my rss feed to be notified when I post them.

Source Code

Full source code is available here: WixServiceInstallerExample.zip (184.90 kb)

Did you find this post useful or interesting? I'd be really grateful if you could return the favour by clicking this google link:



The web.config XDT transformations used in Visual Studio’s web projects are great for ensuring you don’t end up releasing say, a release version of your code pointing to the test database (yeah, that one with all the amusing names and phrases in it). Unfortunately this functionality wasn’t extended to the app.config. Luckily though we can add it in with minimal hassle, read on for the knowledge.

A quick pre-warning

The visual studio team are working on putting this functionality into the next version as standard, and the hacks we are putting into place here could make the upgrade a bit trickier. Personally I think that undoing these changes later is a small price to pay for the benefits you’ll get now. Just don’t come hunting me down when visual studio 2012/13/whatever gets released!

Setting your project up for transformations

This section is all about setting up the project as a whole. I’d advise doing this on a clean project and then using this as your project template so that it’s all ready and waiting for you.

Firstly, you’ll need to create a new Windows Service project. Now right click on the project in Solution Explorer and select Unload Project. This will grey out the project and mark it as unavailable. Now right click again and select Edit [projectname]:

This will load up the msbuild file which defines your project. All of the funky stuff we’re about to do will happen in here. Don’t worry if you don’t know much (or anything) about msbuild though, you’ll pick it up quickly.

Take a look at the first PropertyGroup node at the top of the file. This is defining some top-level information for the compiler to use. Add the following at the end of it. Right after the FileAlignment node:

<ProjectConfigFileName>App.config</ProjectConfigFileName>

This will be used by the existing transformations functionality to know which files to transform. Next, scroll right to the bottom of the file. You should see an Import node pointing to Microsoft.CSharp.Targets. These import nodes work just like a using statement in c# - we’re importing some existing functionality. So add a new Import node below to bring in the web publishing stuff, which includes the transformation targets:

<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.targets" />

This is actually all we need to do to get transforms happening when you build against the TransformWebConfig target, but currently they won’t be written to your output directory. Instead they will appear under obj\x86\Debug\TransformWebConfig. We just need to do a little bit of msbuild code to copy them to the output directory for us.

Right at the bottom of the file, just before the closing </Project> tag enter the following:

<Target Name="TransformAppConfig">
  <CallTarget Targets="TransformWebConfig" />
  <Copy SourceFiles="$(TransformWebConfigIntermediateLocation)\transformed\App.config"
   DestinationFiles="$(OutDir)\$(AssemblyName).exe.config"
   Condition="Exists('$(TransformWebConfigIntermediateLocation)\transformed\App.config')" />
</Target>

This creates our own target, which runs the hacked TransformWebConfig target and then copies any created transformation over the version in our build output path – easy!

Gotcha – OutputPath vs OutDir (MsBuild junkies only!)

It is important that you use the OutDir value to copy the transformed config to here. OutputPath will work fine for local builds but if you try and automate it through TFS Team Build it will mysteriously disappear. Using OutDir will work fine though.

Adding config transformation files

We’re ready to actually add our config files now so exit the msbuild file and reload the project. Create two config files called App.config and App.Debug.config. In the properties for each ensure their Build Action is set to Content so that they get picked up by the TransformWebConfig target. Next open the App.Debug.config and modify the Configuration with a new namespace declaration:

http://schemas.microsoft.com/XML-Document-Transform

This will tell the TransformWebConfig target that this is an XDT transformation rather than a normal config file. Add a transform to the file too so that we can test that everything is working (I’m going to assume you know how to use the transforms if you’re reading this!).

Building the project

Now all we need to do is run the build. For this you’ll need to open the visual studio command prompt and navigate to the folder containing you project file. Enter the following (assuming your project file is called FakingWebConfigService.csproj):

msbuild FakingWebConfigService.csproj /t:TransformAppConfig

With any luck you should see a load of output messages followed by a green Build Succeeded message. Take a look at your project output and your transformed config file will be sat there – magic!

Tidying up solution explorer

We’ve got a nicely working solution now, but it would be nice if we could get the transforms to be a step down the ‘tree’ from the main config file like they do in web apps. This only requires a little bit of hacking so let’s do it now. Unload the project and open its msbuild file. Locate the Content node for each of your transform files and tell it to depend on the main App.config file:

<Content Include="App.Debug.config">
  <DependentUpon>App.config</DependentUpon>
</Content>

That’s it! Reload the project and your transforms should show up in the right place.

How to fire off the transformation process

There are a few options for when to fire off the transform process which I’ll go over briefly:

Keep running it from the command line

Perfectly reasonable, but it means each member of the team needs to know the target name and how to use the command line msbuild. It also means specifying configurations manually – you’d have to add a /p:Configuration=Release argument to the msbuild call to do a release version for example. But it works and it’s better than what we had before.

Run it on every build

We can make it run on every build simply by adding an AfterTargets attribute to our Target node in the msbuild file:

<Target Name="TransformAppConfig" AfterTargets="Build">

Again, this works fine and is more foolproof than running from the command line. My issue with it is that web projects don’t do this on build – only deploy. I think the inconsistency is asking for trouble, but I’m sure it works fine for some teams.

Run it as part of a TFS Build

You can get Team Build to run the TransformAppConfig target for you as part of a central build. This is much better than the previous two options (in my opinion anyway) and is easy to set up. I’ll assume that you know how to set up a new build definition (if not check out the first part of my post on Continuous Deployment). All you then need to do is go into the Process tab, drop down the advanced node and enter /t:TransformAppConfig /p:Configuration=YOURCONFIGURATION into the MsBuild Arguments box:

When you run the build now it will transform your configs for you.

Include it in your WiX installer

This is my favoured solution. It is perfectly possible to cause the config transformations to happen as part of the build cycle of a WiX installer. I’ll went through this technique in my next post on creating Windows Service installers in WiX which should be posted in the next week or so. If you’re new to WiX then get with the program and check out my introductory series on WiX and deployment.

Thank you ladies and gentlemen, and goodbye!

Hopefully this post has been of help to those of you frustrated by the partial config transformation implementation in Visual Studio 2010. If you’d like to be informed when the next step posts on this topic are released you could always subscribe to my RSS feed.

Source Code

Full source code is available here: FakingWebConfigService.zip (29.68 kb)

Did you find this post useful or interesting? I'd be really grateful if you could return the favour by clicking this google link:



Month List

Sign in

Top Programming Sites Vote for me at Fuelmyblog