Posts Tagged ‘ Tips ’

ClickOnce Master Build

Previously I have posted about how to publish a ClickOnce release for multiple environments. Whilst this works well there are two reasons (that I can think of) why it is not appropriate or good enough.

First, you or your manager may be a bit of a purist in terms of releasing to Production ‘exactly’ what has been tested, specifically the exact same compiled assemblies.

Second, you may need to create a release for several different customers who each have several of their own ‘environments’ but you do not want to provide them with your source code.

Third…both of the above. :-)

When you ‘Publish’ with MsBuild it requires the source code and it recompiles your assemblies. So how can we easily create releases for multiple environments but only Publish once? This posting offers one solution which has worked for me in the past, it’s not a complete ‘how to’ guide I just wanted to cover the idea and problems I ran into along the way.

Master Build Environment

Make sure you take a look at my previous posting as this process builds on those ideas.

One of the final steps of this process is to re-sign the setup.exe, however in order for that final step to work you have to change one of your ClickOnce deployment settings in your .csproj file.

<SignManifests>false</SignManifests>

SignManifests must be false! Otherwise the signtool gets confused when re-signing the setup.exe. Check this out for a bit more info on this known issue.

So the first thing to do is create a BuildEnvironment called Master. The configuration details of this (web service addresses or database connnection strings etc) should point to nowhere, i.e. if someone deployed this build then it should not work. Publishing this is easy just follow the steps defined in the multiple environment posting, the process of creating a release with environment specific information that is tricky.

MsBuild Changes for Master Build Publish

When you publish with ClickOnce it creates a folder ‘Application Files’ where it puts the contents of the release. The space in that file name causes a problem with MAGE when re-signing the app. Now the only way I could see to change this was to cut and paste the _CopyFilesToPublishFolder target from Microsoft.Common.Targets and modifying the _DeploymentApplicationFolderName property in my version to remove the space. I think it is a really BAD idea to change the Microsoft.Common.Targets directly so that option was ruled out immediately and as you can see in the code below, Application Files (version below already has the space removed) is a hardcoded value, not a settable property…therefore even though it is ugly, cut and paste seems the only viable solution.

      <!--
	  This Target code has been cut and paste from Microsoft.Common.Targets.
	  Only ONE thing has been changed.
	  Application Files changed to ApplicationFiles (i.e. the space removed)
	  The only other way of changing that value is to modify Microsoft.Common.Targets
	  which is a really bad idea (would have to ensure this was changed on ALL machines.)

	  The space causes a problem when re-signing the app with MAGE
    ============================================================
                                        _CopyFilesToPublishFolder
    ============================================================
    -->
    <Target
        Name="_CopyFilesToPublishFolder">

        <!-- Compute name of application folder, which includes the assembly name plus formatted application version.
             The application version is formatted to use "_" in place of "." chars (i.e. "1_0_0_0" instead of "1.0.0.0").
             This is done because some servers misinterpret "." as a file extension. -->
        <FormatVersion Version="$(ApplicationVersion)" Revision="$(ApplicationRevision)" FormatType="Path">
            <Output TaskParameter="OutputVersion" PropertyName="_DeploymentApplicationVersionFragment"/>
        </FormatVersion>

        <PropertyGroup>
            <_DeploymentApplicationFolderName>ApplicationFiles\$(AssemblyName)_$(_DeploymentApplicationVersionFragment)</_DeploymentApplicationFolderName>
            <_DeploymentApplicationDir>$(PublishDir)$(_DeploymentApplicationFolderName)\</_DeploymentApplicationDir>
        </PropertyGroup>

        <!-- Copy files to publish folder -->
        <Copy
            SourceFiles=
                "@(_ApplicationManifestFinal);
                @(_DeploymentResolvedManifestEntryPoint);
                @(_DeploymentManifestFiles);
                @(ReferenceComWrappersToCopyLocal);
                @(ResolvedIsolatedComModules);
                @(_DeploymentLooseManifestFile)"
            DestinationFiles=
                "@(_ApplicationManifestFinal->'$(_DeploymentApplicationDir)%(TargetPath)');
                @(_DeploymentManifestEntryPoint->'$(_DeploymentApplicationDir)%(TargetPath)$(_DeploymentFileMappingExtension)');
                @(_DeploymentManifestFiles->'$(_DeploymentApplicationDir)%(TargetPath)$(_DeploymentFileMappingExtension)');
                @(ReferenceComWrappersToCopyLocal->'$(_DeploymentApplicationDir)%(FileName)%(Extension)$(_DeploymentFileMappingExtension)');
                @(ResolvedIsolatedComModules->'$(_DeploymentApplicationDir)%(FileName)%(Extension)$(_DeploymentFileMappingExtension)');
                @(_DeploymentLooseManifestFile->'$(_DeploymentApplicationDir)%(FileName)%(Extension)$(_DeploymentFileMappingExtension)')"
            SkipUnchangedFiles="true"
            OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)"/>
        <Copy
            SourceFiles="@(_DeploymentManifestDependencies)"
            DestinationFiles="@(_DeploymentManifestDependencies->'$(_DeploymentApplicationDir)%(TargetPath)$(_DeploymentFileMappingExtension)')"
            SkipUnchangedFiles="true"
            OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)"
            Condition="'%(_DeploymentManifestDependencies.DependencyType)'=='Install'"/>
        <Copy
            SourceFiles="@(_ReferenceScatterPaths)"
            DestinationFiles="@(_ReferenceScatterPaths->'$(_DeploymentApplicationDir)%(Filename)%(Extension)$(_DeploymentFileMappingExtension)')"
            SkipUnchangedFiles="true"
            OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)"/>
        <FormatUrl InputUrl="$(_DeploymentApplicationUrl)">
            <Output TaskParameter="OutputUrl" PropertyName="_DeploymentFormattedApplicationUrl"/>
        </FormatUrl>
        <FormatUrl InputUrl="$(_DeploymentComponentsUrl)">
            <Output TaskParameter="OutputUrl" PropertyName="_DeploymentFormattedComponentsUrl"/>
        </FormatUrl>
    </Target>

Note that the space only causes a problem with MAGE.exe, it does not cause a problem with MAGEUI.exe! MAGEUI.exe happily re-signs the release even with the space in the directory name. We cannot use MAGEUI.exe however because this process needs to be automated, if you use MAGEUI.exe someone has to run it and manually set the values…not good enough.

It took a while to figure out that the space in the dir name was causing a problem so I hope this saves someone else some time…

Creating a Release

Ok so now we have a published build that no one can use, the first place we want to release to will be QA, then from there UAT so what we need now is an automated way of creating a release configured for these environments.

Creating a release should be as easy as publishing a build, it should be a one step process.

Create a batch file ‘createRelease.bat’ which performs all of the necessary steps. Those steps are:

  1. Create or clean the ‘working’ directory.
  2. Copy the master build into the working directory.
  3. Run the MsBuild steps.
  4. Create a zip file of the new release. (not necessary just makes it a little easier to move the releases around)

Run the MsBuild Steps

Add a new target in your customized.targets file, ModifyMasterBuildForEnvironment.

<Target Name="ModifyMasterBuildForEnvironment" DependsOnTargets="SetPropertyValues; ConfigureForEnvironment; RecreateManifests" />

SetPropertyValues

There are a few properties which need to be set for the resigning process.

	<Target Name="SetPropertyValues" >
		<Message Text="FullReleasePath $(FullReleasePath)" />

		<MSBuild.ExtensionPack.Framework.TextString TaskAction="Replace" OldString="$(ApplicationVersion)" OldValue="." NewValue="_">
			<Output PropertyName="buildX" TaskParameter="NewString"/>
		</MSBuild.ExtensionPack.Framework.TextString>

		<PropertyGroup>
			<WorkingDirectory>WorkingDirectory</WorkingDirectory>
			<FullReleasePath>$(WorkingDirectory)\ApplicationFiles\$(AssemblyName)_$(buildX)</FullReleasePath>
			<ApplicationManifestName>$(AssemblyName).exe.manifest</ApplicationManifestName>
			<ApplicationManifestPath>$(FullReleasePath)\$(ApplicationManifestName)</ApplicationManifestPath>
			<DeploymentManifestPath>$(WorkingDirectory)\$(DeploymentManifestName)</DeploymentManifestPath>
			<CertFileName>cert\pmsKey.pfx</CertFileName>
			<ProviderFullUrl>$(ProviderBaseUrl)$(DeploymentManifestName)</ProviderFullUrl>
		</PropertyGroup>
	</Target>

ConfigureForEnvironment

Add your own target(s) here that does the work to configure the release for a given environment. e.g. update the config file setting database connection strings or web service urls.

RecreateManifests

Now that you have modified the published release it is no longer a valid ClickOnce release. Try to install it and it will fail. What you need to do is update and re-sign the manifests (the Application Manifest and the Deployment Manifest). You also need to update and resign the Setup.exe!

	<Target Name="RecreateManifests">

		<!-- Application manifest update and resign -->
		<Message Text="Removing .deploy suffix..." />
		<RenameFiles DirectoryPath="$(MSBuildProjectDirectory)\$(FullReleasePath)" RemoveSuffix=".deploy" IncludeSubDirectories="True" />
		<Message Text="Updating application manifest" />
		<Exec Command="mage -Update $(ApplicationManifestPath) -FromDirectory $(FullReleasePath) -ToFile $(ApplicationManifestPath)" />
		<Message Text="Adding .deploy suffix..." />
		<RenameFiles DirectoryPath="$(MSBuildProjectDirectory)\$(FullReleasePath)" AppendSuffix=".deploy" IncludeSubDirectories="True" ExcludedFiles="$(ApplicationManifestName)" />
		<Message Text="ApplicationManifestPath $(ApplicationManifestPath)" />
		<Exec Command="mage -Sign $(ApplicationManifestPath) -CertFile $(CertFileName)" />

		<!-- Deployment manifest update and resign -->
		<Message Text="Updating Deployment manifest (ProviderFullUrl=$(ProviderFullUrl))" />
		<Exec Command="mage -Update $(DeploymentManifestPath) -AppManifest $(ApplicationManifestPath) -ProviderUrl $(ProviderFullUrl)" />
		<Message Text="DeploymentManifestPath $(DeploymentManifestPath)" />
		<Exec Command="mage -Sign $(DeploymentManifestPath) -CertFile $(CertFileName) " />

		<!-- Setup.exe update and resign -->
		<Message Text="Updating $(WorkingDirectory)\Setup.exe..." />
		<Exec Command="$(WorkingDirectory)\Setup.exe /url=$(ProviderBaseUrl)" />
		<Message Text="Signing $(WorkingDirectory)\Setup.exe..." />
		<Exec Command="signtool sign /v /f $(CertFileName) $(WorkingDirectory)\Setup.exe" />

	</Target>

Note that before Updating the Application Manifest we have to remove the .deploy extension from the file names, then add the .deploy extension back onto the file names before Signing. (if you have configured ClickOnce to NOT append .deploy then obviously just remove these steps).

RenameFiles Task

To get the renaming behaviour I wanted I had to create my own MsBuild task. You will need to do the same, it’s a little crude as I had limited time but this is the simple little task I created….

    public class RenameFiles : Task
    {
        public override bool Execute()
        {
            var directory = new DirectoryInfo(DirectoryPath);
            if(directory == null)
                throw new ApplicationException(string.Format("Directory path is invalid: {0}", DirectoryPath));
            Console.WriteLine(string.Format("DirectoryPath:{0}", DirectoryPath));

            ProcessDirectory(directory);

            return true;
        }

        private void ProcessDirectory(DirectoryInfo directory)
        {
            RenameAllFiles(directory);
            if(! IncludeSubDirectories)
                return;

            var subs = directory.GetDirectories();
            foreach (DirectoryInfo sub in subs)
                ProcessDirectory(sub);
        }

        private void RenameAllFiles(DirectoryInfo directory)
        {
            var files = directory.GetFiles();

            if(files.Length == 0)
                return;

            foreach (FileInfo file in files)
            {
                string fileName = file.Name;
                if (ExcludedFileList.Exists(name => name.Equals(fileName, StringComparison.InvariantCultureIgnoreCase)))
                {
                    Console.WriteLine(string.Format("{0} is excluded from renaming.", file.Name));
                    continue;
                }
                Console.WriteLine(string.Format("Renaming {0}", file.Name));
                RemoveExtensions(file);
                AppendExtensions(file);
            }
        }

        private void RemoveExtensions(FileInfo file)
        {
            if (string.IsNullOrEmpty(RemoveSuffix))
                return;
            file.MoveTo(file.FullName.Replace(RemoveSuffix, string.Empty));
        }

        private void AppendExtensions(FileInfo file)
        {
            if (string.IsNullOrEmpty(AppendSuffix))
                return;
            file.MoveTo(string.Format("{0}{1}", file.FullName, AppendSuffix));
        }

        public string DirectoryPath { get; set; }
        public string RemoveSuffix { get; set; }
        public string AppendSuffix { get; set; }
        public string ExcludedFiles { get; set; }
        public bool IncludeSubDirectories { get; set; }

        private List<string> ExcludedFileList
        {
            get
            {
                if (excludedFileList == null)
                    excludedFileList = CommonFunctions.CreateListFromCommaSeparatedString(ExcludedFiles);
                return excludedFileList;
            }
        }
        private List<string> excludedFileList;
    }

Summary

There are a number of other little steps we do to make this release process work here but these are really specific to how we want it to work. What I have outlined above is the core steps involved including some problems you will face and their solutions. I hope this posting helps you get over the most challenging of the hurdles you will face in setting a smooth ‘Master Build’ release process but there is still alot of extra steps you will need to create yourself to get it working end to end.

One thing I would recommend is, once you have a working createRelease.bat (note it doesn’t have to be a batch file, could be an exe or powershell or whatever works best in your situation) then create a createReleaseALL.bat file. In there make n calls to createRelease.bat…one for each environment you need to create releases for. That way if you have a dozen environments you can still create a release for all of them with a one step process :-)

I have had to do this fairly quickly so if something is not clear please let me know and I will see if I can improve the post over time.

Final note – It does take time to set this up and get it working correctly, but it is so worth it! Over time you will save much more time than you spent setting it up, guaranteed!

Additional Resources

A few links which helped me to piece this puzzle together…

ClickOnce with bootstraper setup.exe need to change URL without any build

packaging-a-clickonce-server-deployment

Batch File Error

Weird Characters Causing Batch File Error

is not recognized as an internal or external command, operable program or batch file

That’s the error I was getting, weird considering the only thing (after removing everything else) in the file was @ECHO OFF. The ‘weird’ characters are shown in the little snippet below

After a bit of head scratching I opened notepad++, switched on ‘Show all characters’…nothing strange there. I had seen this before but I can never remember….then I did! Remember that is, the problem was the file, when it was created (by visual studio…as a txt file), was created with UTF-8 encoding, needs to be ANSI for batch files (or at least it can’t be UTF-8).

Green indicates ‘good’ encoding for batch files, red bad.

With notepad++ you can see what the encoding is (under Format) and change it. I changed my batch file to ANSI and voila it works!

I’ve blogged it so I can remember next time :-)

Learning PostSharp

AOP from the Trenches

Aspect Oriented Programming feels alot like applying style sheets to a web site. Changing your ‘aspects’ will change the way your program works without having to make any changes to your code.

A few months ago I saw a great demo on PostSharp by Omar Besiso and I remember thinking, must ‘check that out someday‘.

Well amazingly, someday just rolled around and I started looking at PostSharp more closely and see if it could help me. My project needs logging and exception handling to be added so we are using the enterprise library blocks and I was keen to see how PostSharp might make my life easier.

The documentation on PostSharp is good but it’s a bit thin on best practice examples and little ‘gotchas’ so this blog entry is just a little collection things I am learning along the way…note that I am completely new to Aspect Oriented programming so please correct me if I say somethings really stupid or ignorant :-)

There are also a couple of good articles on CodeProject to help you get started.

1. Restart Visual Studio

So I installed PostSharp, followed the quickstart sample exactly, compiled…didn’t seem to do anything. Ok so open up Reflector, did it actual make any changes? Nope.

After scrounging around the documentation for a while and trying various things to figure out what I was doing wrong I finally went back to the website ‘getting started’ section and in step two it clearly says ‘restart visual studio’.

Of course! Did that…all works.

And how good is this….I tweeted a hotip to restart VS after installing PS and within an hour I got message from Gael Fraiteur (the author) saying he’d raised a bug to warn users to restart VS after installing!

2. Don’t Try/Catch

These next few points all relate to handling exceptions with Aspects.

Let’s say you add this attribute to a method (and you’ve set them up properly):

[MyExceptionHandlerAspect("MyExceptionPolicy")]
public void MyMethod()
{
try
{
// exception happens here
}
catch(Exception ex) {// do something with the error
}
}

If you then do a try/catch like I have in the method then that try/catch takes over and the Aspect no longer ‘handles’ the exception. Which is good I think, you want to be able to ‘override’ what the aspect does in some instances. It feels a bit strange though, NOT wrapping a potentially naughty bit of code in a try/catch block, you have to have faith that the aspect will do its job.

NOTE: I’m not saying never use a try/catch block anymore, you will often still want to catch and specifically handle some exceptions.

3. Where Does FlowBehavior.Continue From?

I used eventArgs.FlowBehavior = FlowBehavior.Continue; and I expected the code execution to continue from (next line) where the exception occured. It didn’t, it returned to the line of code that called the method where the exception occurred. Consider the following:-

exceptionContinue

When the exception occurs in the Third method, my aspect handles it by doing whatever and then sets eventArgs.FlowBehaviour = FlowBehaviour.Continue; The result of this is execution continues in the Second method…and the Fourth method is not called.

Cool, not quite what I expected but it’s fine, the main thing is understanding how it works.

So what happens if we move that attribute?

exceptionContinue2

Well, here exception occurs in Third, but the aspect handles it in Second and so execution continues in First. Nice, just be aware of where you are putting the attribute.

We could also do this…

exceptionContinue3

Here, the exception occurs in Third and the aspect handles in Third also, so execution resumes in Two.

Note you can also put the attribute on the class, which saves you from putting it on individual methods. Seems to me that the best place to wire up the exception handlers are in the AssemblyInfo.cs files. This is what you need to add:-

[assembly:  MyExceptionHandlerAspect("MyExceptionPolicy")]

which to me is also much less confusing than attributes here and there randomly scattered through your code. Plus one of the big advantages of using PostSharp is that there is almost zero change required to existing code. So my ‘guess’ as to best practice here is to only apply an Exception Handling Aspect Attribute to individual methods when there is a very specific need to handle raised exceptions differently (i.e. use a different aspect or perhaps the same aspect with different parameters supplied – eg. in the case above I might want to use a different exception policy).

4. Continue After Exception – ‘Problem’

Just to be clear I don’t see this as a problem with PostSharp, it’s just a stumbling block I ran into.

I’m using the enterprise library, specifically for this problem I’m using the Exception Handling block. I have a policy to ‘handle’ System.Exception’s by logging them and then rethrowing an new exception that just wraps the original one.  Since all exceptions derive from System.Exception this handler will catch all exceptions except those that have their own explicitly defined handler (including the null reference exception below).

To the relevant AssemblyInfo.cs f iles I have added [assembly:  MyExceptionHandlerAspect("MyExceptionPolicy")].

Now I am also using CAB and discovered this ‘continue problem’ in a command handler.

commandHandler

I’ve added a dodgy bit of code that forces a null reference exception.

So, my PostSharp aspect looks like this

exceptionAspect

and it nicely does intercept my null reference exception but what happens when the call to HandleException is made? The enterprise library takes over and does it’s thing, logs and wraps the exception in a new exception which is returned in exceptionToThrow….great, so far so good.

The next thing that is going to happen is exceptionToThrow…will be thrown. But let’s see what the call stack looks like BEFORE it gets rethrown….

callStack1

At the top of the stack is the breakpoint I set in the OnException method in the OnExceptionAspect class. No problem, next is the OnDoSomething method where the exception occured. The next three are all CAB related (including the Infragistics line), then before that is essentially the call to Start().

So what happens is my policy causes the rethrow of the new exception, which gets thrown ‘back’ to OnExecuteAction in CAB code…which (currently) doesn’t have any PostSharp goodness to automatically wire up our exception handling policies. This means the exception will keep getting bubbled up the stack until it hits either a try/catch (that doesn’t rethrow) or an assembly with an exception aspect defined (PostSharp goodness). Let’s see what the call stack looks like after the new exception gets thrown.

callStack2

So it’s now Continuing execution from the next point that ‘handles’ the thrown exception. Problem is, that’s in the Start method and once execution continues beyond the call to base.Start then the application shutsdown (just the way a CAB app works).

Solution

Ok so I’ve gone away and thought about this for a bit and my solution is to change my policies. I now have a UI Layer Policy which never rethrows, it just logs and reports a message to the user. So all of my Modules and the main Shell use this Policy, there are other policies for other layers where required and an all purpose one that still does the log, wrap and rethrow…which eventually gets handled by some UI layer handler.

5. Lose Edit and Continue

I guess, due to the nature of how PS works (‘injecting code’, creating delegates etc Post compilation), you’ll see this kind of message popping up alot…

editAndContinue

when you try to edit code whilst debugging. So no more edit and continue if you add any Aspect Attributes to your class. What I’ve discovered so far is that if you have any methods, properties etc  in a class marked with an aspect attribute then you won’t be able to edit and continue anywhere in the class. If the class is ‘clean’ of aspects then you can edit and continue.

This is a heavy price to pay in my eyes, fine if your app is small and fast to start but my last app took 3 mins to build and compile so having to restart much more frequently when debugging would be very frustrating. Fortunately this problem is very easy to almost completely eliminate.

This straight from the documentation:

Disabling PostSharp

It can happen that you have an assembly referencing PostSharp.Public.dll, but that does not need to be processed by PostSharp. This is the case, for instance, when your assembly contains only aspects. Aspects don’t need to (and can not) be themselves transformed.

The easiest way to disable PostSharp in a project is to define the compilation symbol (aka constant) SkipPostSharp.

Therefore, it is possible to have a project with many build configurations and enable PostSharp in only some of these.

So what I’ve done is created a DebugWithPostSharp build configuration, which is a normal debug build. Then on the Debug configuration I added the SkipPostSharp symbol to the relevant projects. The Release configuration always gets PostSharp (really important because unit tests are run against the Release build).

Summary

There are so many useful ways you could use PostSharp, just make sure you take the time to thoroughly understand how it does it’s thing….experiment alot! This is just a few things I have learnt along the way, if I find anymore (and some spare time…) I’ll do a part 2.

VSS to SVN Migration

Subversion

This post has a few tips and insights from my recent experience of migrating from VSS to Subversion.

The first thing to note is that version control in SVN has some conceptual differences to VSS and it is worth spending some time reading the SVN Book to understand these differences. Understanding these differences will often give you the ‘why’ for migrating to SVN.

Why did we migrate?

Our shortlist of reasons for migrating:

  1. Branching in SVN is much easier than VSS.
  2. VSS doesn’t always play nicely when disconnected (issues with working folders)
  3. With SVN I can work on n individual bugs in completely separate folders, allowing me to easily work on the bugs independantly (useful for example if you are say writing a new feature against the dev source and urgently need to fix a bug in the production source). Once again VSS has ‘working folder’ issues when you work this way.
  4. Depending on where you store your repository, SVN plays nicely over the internet…fantastic for teams working remotely.

History

If you can migrate without keeping the history then that is the easiest and fastest way.It’s not ideal but you can always still get the history of a file from VSS.

Note:  easiest != best.

If you want to maintain history I used this migration tool with great success.

A few things about the migration tool…

  1. Play with it on a mini repository first! (and remember it changes your system clock!)
  2. The most time consuming part of the process is when it does a ‘get history’ from VSS. Drove me nuts because you get the history with the VSS gui and you get the results almost immediately, get the history from the cmd line and it took 40-60 seconds! I did search for some cmd line switch to help but no joy.
  3. It’s c++ so I was too scared to make any changes and it worked fine but the source is available if you need to tweak it.
  4. I had some corruptions in VSS, this causes the migration tool to pause. Make a note of the file, ignore and continue.
  5. Depending on the size of your repo you may want to import from a logical sub project.

You will also need to do the migration on a separate dedicated machine because it changes the system clock when committing. So yes the migration tool must run on the same box as the repository it is committing to. Once the migration is complete you can either copy your flashy new SVN repo to it’s new ‘production’ home or if you have set it up with a Berkely DB then you’ll need to export and import. The SVN Book has plenty of details on ‘how’ to export/import etc.

This blog entry helped me as well.

When You are Done

Test

I strongly recommend you run the app through a full test phase once you are done to ensure you ‘got everything’.

Point to the New Location

If you have a CI server setup you’ll need to configure to point to the new repo, same goes for you and your team members.

Maybe change the location of the old VSS repo so that no one accidentally checks in. Don’t delete it though.

Determine if Internet Connection Available

Smart Client

Be careful with your logic when using

NetworkInterface.GetIsNetworkAvailable()

This tells you if any network is available…which doesn’t necessarily mean that you can connect to the internet!

For example if in my app if I unplug my network cable the method returns false…until I plug in my mobile device and suddenly (and accurately) it returns true. Plugging my mobile in does not mean I have internet access BUT IT MIGHT. 

The best way to check for an internet ready connection is to make some call out to the internet and see what happens, there’s a few suggestions for which approach, I like this one:-

MARS to the Rescue

WCF Concurrency

When you create a WCF web service you most likely want it to have a ConcurrencyMode of ‘Multiple‘ so that your service will scale. Check this out if you need some convincing on this.

Here’s an error I recently ran into when multiple threads started hitting my database:

There is already an open DataReader associated with this Command which must be closed first.

I’m using enterprise libraries Data Access Block and my DB is an SQL Server 2005. For once the fix was really easy, I just had to change my connection string to include MultipleActiveResultSets=True.

That’s it!

Check out this link that helped me figure it out and this MSDN article more details.

A nice easy immediate fix for a problem I thought would require some re-engineering. Why can’t all problems be like that :-|

Getting Started With Continuous Integration

In Less than Ten Minutes!

This article is all about how you can easily get up and running with CI in less than 10 minutes!

There are really two separate parts to gettings started with CI (and therefore two parts to this article).

  1. Setting up the build server and
  2. Creating the build (e.g. Nant files)

Then there are the issues you will face and need to find some form of resolution for.

First you need to download and install these tools:-

  1. Cruise Control
  2. CCTray (same location as CruiseControl)
  3. NAnt
  4. NAntGenie

And Add the path to these exe files in your Environmental Variables

  1. NAnt
  2. msbuild (something like ‘C:\Windows\Microsoft.NET\Framework\v3.5′)
add path to NAnt and msbuild

add path to NAnt and msbuild

Presumed Knowledge

These are demo notes from a ‘lightning talk’ I gave at the Sydney Alt.Net user group in February.

In the talk I had only 10 minutes, so I made the presumption that all in attendance knew what CI was, if you don’t then check out these links, they will get you started:-

Martin Fowler

Extreme Programming Rules

The Build Server

I’ve always used Cruise Control as it has always met my needs, is free, simple to get up and runnning and pretty much trouble free. For the demo I used version 1.4.2, when you download it, make sure you also get CCTray.

There are a number of other build servers out there that are definately worth looking at and comparing, I’m especially keen to try out Jetbrain TeamCity…when I find some time. It doesn’t matter which one you choose.

Setup CCTray

  1. Install Cruise Control (keep all of the default settings)
  2. Install CCTray (start it)
  3. Ok so now open CCTray settings and add (try to) a new build server.

ci_cctraysetupbuildserver2

Connect directly using .Net Remoting is the default, change it to Via the CruiseControl.NET dashboard (just a recommendation).

Click OK and you should see at the bottom a message saying, ‘Failed to connnect to server….’. We going to fix that right now.

Configure The Server

First thing we need to do is configure the service.

  1. Go here [C:\Program Files\CruiseControl.NET\server] or wherever you chose to install it.
  2. Open ccnet.config
  3. This is where things get personal. The example below is a basic configuration to point to an SVN Repository. The documentation on the Cruise Control website is concise and easy to follow so if your needs are different you’ll have no problems figuring out what you need to do.
  4. Add a ‘Project’ with the follow config sections:
    1. Trigger
    2. Source Control
    3. Labeller
    4. Build Task
    5. Publishers
  5. Save and double click on ccnet.exe
  6. You now have a working automated build! (well sort of…we still need to do the second part of the process, THE BUILD!)


TIP: Don’t forget to change the verbosity of the output to INFO (it’s currently DEBUG). Don’t worry CC will remind you every time it starts and tell you where the config file is that you need to change.

This is the contents of the config used in the demo, change it to suit your environment and save. Make sure:-

  • The path to NAnt.exe is correct.
  • The trunkURL for your repository is correct (or replace with appropriate section for your repository)
  • Working directory and publish from directories are the same.
  • You may need to make sure the SuccessfulBuilds directory exists before running…

Create the Build Files

The other big piece of the puzzle is the master.build file and the associated myProject.build files.

My tool of choice here is NAnt and I use (wrote) this generator to automatically create my build files and the master.build.

To create these use NantGenie (be sure to read the Notes section)…or you can write them from scratch.

RUN

That’s it you should be good to go. Start ccnet, checkin a change to your source, the build should start and run your new master.build file!

White Lie

Ok so here’s where I have to come clean. It may have been a little white lie when I said that you could get CI up and running in less than 10 minutes. You are going to run into some other issues that is going to make it take….longer.

I discuss some of these issues in this article. But in a nutshell these will be the biggest thorns in your side:-

1. Database changes

Ideally when you check your code in, you are checking in a ‘unit of work’. This could include some app code changes AND some database changes, e.g. a new column and some data. Your tests rely on the database being up to date, so how do you ensure that the database on the build server DOES get updated when you checkin?

I’ve created an automated Db updater and put it on codeplex, it works for me. Check it out and I recommend looking at what other alternatives might work in your unique environment.

2. App.Config changes

So what is the best way to ‘manage’ the app.config files?

I don’t have a definitive answer for this one but I think that the following conditions are ‘desirable’ when planning how to manage your app.config.

  1. Like other code, you don’t want to have duplicates (including cross project).
  2. You only want data in there that is relevant to the project.
  3. Must be under source control.
  4. You want to be able to define what the variable contents should be in one place only. (where doesn’t matter too much as long as it is only one place)
  5. When you make changes to your App.Config, you want to make them whilst you are working on your relevant code change and then forget about it once it has been checked in. By ‘forget about it’ I mean you don’t have to remember to update the file on the UAT box when promoting a new build there and then remember to do the same change to Production…

All of this is tricky with an automated build and multiple environments.

3. People

Probably your biggest hurdle will be convincing people that CI helps and that they need to CHANGE their habits a little (sometimes alot) in order to make it work.

Tips

  • If you’re struggling to figure out why the damn build is broken, the best place to look is here [C:\Program Files\CruiseControl.NET\server\YourProject\Artifacts\log].
  • Also, don’t forget you can also turn the verbosity of the CC server back to DEBUG.

Conclusion

Hopefully this article will help reduce the time it takes to get some CI action happening in your team. Yes it is going to take more than 10 minutes but I guarantee you it is worth the effort and once you start using an automated build you simply refuse to go back to the chaos that preceded it!

Studio Version Selector Failure

The Questions

Are you clicking on your .sln file and nothing is happening?

Is it using the Visual Studio Version Selector (VSVS) to try to open your solution??

Do you only have ONE VERSION of visual studio on your machine???

Are you thinking “this VSVS is a piece of rubbish! How can it fail to find and use my ONE installed version!!”????

Well worry no more, the solution is simple (well, assuming you have the same problem as I did).

The Fix

  1. Open your .sln in wordpad
  2. Remove the blank line(s) from the top of the file
  3. Save
  4. Done!

It seems that VSVS is not capable of searching beyond the first line of the file for the version number. Is it just me or that lame?

WCF Concurrency Mode Tip

Don’t Keep the Default Setting!

When you mark your WCF endpoints with the ServiceBehaviour attribute, are you setting the ConcurrencyMode to Multiple?

The default is Single, meaning only a single thread will invoke the instance method on the reciever object. This may be desirable in certain situations however if you want your service to be scalable then Multiple would be the recommended choice.

Checkout this great article that offers a great explanation of the differences…this is an excerpt from the ‘thoughts’ at the end of the article

ConcurrencyMode should be set to Multiple. In my view, if a receiving application is going to scale, it must set concurrency mode to Multiple. If single or Reentrant are used, messages stack up as they wait to be processed, and that is badness.”

Team Training

Learn From Teaching…

A few months ago we started doing some of our own in team training. Inspired by doing a couple of 10 minute demo’s for the ‘demos happen here’ competition I devised a new (well I’ve never done anything like it before) approach to training the guys in my team (and myself).

Nothing will reinforce your own knowledge about a subject that trying to teach someone else.

So with that in mind, Once per week we get together for 15 minutes only and one person has to deliver a 10 minute (ish) lesson about a specific topic. Instructions are to research on the internet, not just refer to MSDN which offers dry matter of fact details about the subjects, we want lessons from the real world.

A lesson should include (but is not limited to):-

  1. What is it
  2. Why is it cool…or useful
  3. Simple example of use (definately)
  4. Example of misuse (preferably)
  5. Alternatives (other techniques, features etc that could be used instead or in the case of a new language feature what had to be done previously)
  6. Any other ‘interesting’ things you discovered whilst researching the topic
  7. An IDE tip! Can be something native to VS2008 or can be something in resharper…

Topic can also be anything but must be of limited scope given the lesson cannot exceed 10 minutes, some of our topics have included Value vs Reference type, boxing and unboxing, auto properties, lessons specific to implementations in our project. I think it’s good to cover new ground but also what you would expect people to already understand….when you dig deeper into any topic you always find some hidden gems!

The guys enjoy it and come away from each lesson having learnt something. From management perspective it takes guaranteed less than 30 mins a week and their team is constantly improving as a result of it…plus they’ve not had to pay for expensive external training!

One more thing I would suggest, make sure you book in the time in your calenders weeks in advance, otherwise you’ll find that weeks pass when other priorities force the training to be put off till next week…and before you know it it’s Christmas!

Let me know if you try (or have tried) something similar, I’d be interested in hearing your results and opinions :-)

Follow

Get every new post delivered to your Inbox.