Damian Hickey

Mostly software and .NET related. Mostly.

Extension Methods, Guards Clauses and Code Analysis

´╗┐Code Analysis, when encountering this:

public static class FooExtensions
{
    public static void Bar(this Foo instance)
    {
        Guard.Against(instance != null, () => new ArgumentNullException("instance"));
    }
}

...results in a a warning:

CA2208 : Microsoft.Usage : Method 'FooExtensions.Bar(this Foo)' passes 'instance' as the 'paramName' argument to a 'ArgumentNullException' constructor. Replace this argument with one of the method's parameter names. Note that the provided parameter name should have the exact casing as declared on the method.

The problem here is that the 'this Foo instance' paramater isn't a paramater in the usual sense, so I can't use ArgumentNullException or ArgumentException.

Perhaps NullReference exception then? Nope...

CA2201 : Microsoft.Usage : 'FooExtensions.Bar(this Foo)' creates an exception of type 'NullReferenceException', an exception type that is reserved by the runtime and should never be raised by managed code. If this exception instance might be thrown, use a different exception type.

So I've settled on using InvalidOperationException.

 

public static class FooExtensions
{
    public static void Bar(this Foo instance)
    {
        Guard.Against(instance != null, () => new InvalidOperationException("instance"));
    }
}

 

Rename a Visual Studio Project using PowerShell

I seem to be mostly posting about PowerShell these days... but it's because I'm find all these neat ways to make my life easier.

Renaming a project within Visual Studio is easy enough. If, like me, your project folder name is the same as the project and you'd like that to be renamed to. Doing it directly in Windows Explorer will result in a couple of problems: the solution won't find the project so you have to remove and re-add it; projects that referenced the renamed project will have to have their references re-added.

And then your root namespace, assembly name, and assembly title need to be fixed too (assuming these are the same as the project name, which is usually the case).

clickety-clickety-clickety...

The script below solve this problem. It:

  1. hg renames the project file
  2. hg renames the folder
  3. changes the assembly title in AssemblyInfo.cs
  4. changes the assembly root namespace in the project file
  5. changes the asssembly name in the project file
  6. updates all other .csproj file's reference path to the renamed project
  7. updates the path to the renamed project in the solution file
  8. is designed to be called from the same working folder as the .sln
  9. expects all project folders to be immediate child folders of the working folder

Note: This a definite 'works for me' script so will probably need some adjusting to work for you.

function Rename-Project
{
	# designed to run from the src folder
	param(
		[string]$projectName=$(throw "projectName required."),
		[string]$newProjectName=$(throw "newProjectName required.")
	)
	
	if(!(Test-Path $projectName)){
		Write-Error "No project folder '$projectName' found"
		return
	}
	
	if(!(Test-Path $projectName\$projectName.csproj)){
		Write-Error "No project '$projectName\$projectName.dll' found"
		return
	}
	
	if((Test-Path $newProjectName)){
		Write-Error "Project '$newProjectName' already exists"
		return
	}
	
	# project
	hg rename $projectName\$projectName.csproj $projectName\$newProjectName.csproj
	
	# folder
	hg rename $projectName $newProjectName
	
	# assembly title
	$assemblyInfoPath = "$newProjectName\Properties\AssemblyInfo.cs"
	(gc $assemblyInfoPath) -replace """$projectName""","""$newProjectName""" | sc $assemblyInfoPath
	
	# root namespace
	$projectFile = "$newProjectName\$newProjectName.csproj"
	(gc $projectFile) -replace "<RootNamespace>$projectName</RootNamespace>","<RootNamespace>$newProjectName</RootNamespace>" | sc $projectFile
	
	# assembly name
	(gc $projectFile) -replace "<AssemblyName>$projectName</AssemblyName>","<AssemblyName>$newProjectName</AssemblyName>" | sc $projectFile
	
	# other project references
	gci -Recurse -Include *.csproj |% { (gc $_) -replace "..\\$projectName\\$projectName.csproj", "..\$newProjectName\$newProjectName.csproj" | sc $_ }
	gci -Recurse -Include *.csproj |% { (gc $_) -replace "<Name>$projectName</Name>", "<Name>$newProjectName</Name>" | sc $_ }
	
	# solution 
	gci -Recurse -Include *.sln |% { (gc $_) -replace "\""$projectName\""", """$newProjectName""" | sc $_ }
	gci -Recurse -Include *.sln |% { (gc $_) -replace "\""$projectName\\$projectName.csproj\""", """$newProjectName\$newProjectName.csproj""" | sc $_ }
}

 

I'm not totally enamoured with the find/replace bits, but they seem to work.

Any improvements will of course be welcomed :)

PSake task to scan and execute all test projects

Another one for the toolbox. This script will execute all scan for all unit test projects and execute them. It assumes that all test project names end with 'Tests', the assembly name is the same as project name, and the project folder is at the solution root.

 

task RunTests -depends Compile {
	$xunitRunner = "tools\XUnitRunner\xunit.console.clr4.exe"
        $project = $_.BaseName
        Get-ChildItem ..\ -Recurse -Include *Tests.csproj | % {
		   .$xunitRunner "$srcDir\$project\bin\Release\$project.dll"
	}
}

 

Simple Powershell script to tidy NuGet's packages.config

This:

param([string]$path = ".")

$packagesConfigFiles = Get-ChildItem $path -Recurse | Where-Object {$_.Name -eq "packages.config"}

if($packagesConfigFiles -eq $null){
	Write-Error "No packages found"
	return
}

foreach($packagesConfig in $packagesConfigFiles){
	Write-Host "Tidying" $packagesConfig.FullName
	$xml = [xml](Get-Content $packagesConfig.FullName)
	$sortedNodes = $xml.packages | select-xml 'package' | Select-Object -expand Node | sort 'id'

	[xml]$sortedXml = "<?xml version='1.0' encoding='utf-8'?>
	<packages>
		$($sortedNodes | % { $_.OuterXml})
	</packages>"

	$sortedXml.Save($packagesConfig.FullName)
}

...will turn this:

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Iesi.Collections" version="3.1.0.4000" />
  <package id="NHibernate" version="3.1.0.4000" />
  <package id="Castle.Core" version="2.5.2" />
  <package id="NHibernate.Castle" version="3.1.0.4000" />
  <package id="FluentNHibernate" version="1.2.0.712" />
  <package id="Microsoft.IdentityModel" version="6.1.7600" />
  <package id="NBuilder" version="2.3.0.0" />
  <package id="FluentMigrator" version="0.9.0.1" />
  <package id="Ninject" version="2.2.1.0" />
  <package id="Ninject.Extensions.Wcf" version="2.2.0.0" />
  <package id="Ninject.Web.Mvc2" version="2.2.0.1" />
  <package id="Common.Logging" version="1.2.0" />
  <package id="Rx-Core" version="1.0.2856.0" />
  <package id="Compare-NET-Objects" version="1.0.2.0" />
  <package id="StoryQ" version="2.0.5" />
  <package id="Moq" version="4.0.10827" />
  <package id="xunit" version="1.8.0.1545" />
  <package id="xunit.extensions" version="1.8.0.1545" />
</packages>

...into this:

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Castle.Core" version="2.5.2" />
  <package id="Common.Logging" version="1.2.0" />
  <package id="Compare-NET-Objects" version="1.0.2.0" />
  <package id="FluentMigrator" version="0.9.0.1" />
  <package id="FluentNHibernate" version="1.2.0.712" />
  <package id="Iesi.Collections" version="3.1.0.4000" />
  <package id="Microsoft.IdentityModel" version="6.1.7600" />
  <package id="Moq" version="4.0.10827" />
  <package id="NBuilder" version="2.3.0.0" />
  <package id="NHibernate" version="3.1.0.4000" />
  <package id="NHibernate.Castle" version="3.1.0.4000" />
  <package id="Ninject" version="2.2.1.0" />
  <package id="Ninject.Extensions.Wcf" version="2.2.0.0" />
  <package id="Ninject.Web.Mvc2" version="2.2.0.1" />
  <package id="Rx-Core" version="1.0.2856.0" />
  <package id="StoryQ" version="2.0.5" />
  <package id="xunit" version="1.8.0.1545" />
  <package id="xunit.extensions" version="1.8.0.1545" />
</packages>

To be honest, I'd prefer if NuGet did this itself.

Signing Mercurial Changesets on Windows

Unfortunately, cryptographic signing of changesets is not a first class citizen in the Mercurial world. Signing of changesets at commit time requires the use of the commitsigs extension. This is how to set it up on windows x64.

Install

  1. Download and install Gpg4win from http://www.gpg4win.org/. Launch
  2. Create a key pair.

Installing commitsigs Extension

  1. Download commitsigs.py and save to desired location. I used %ProgramFiles%\TortoiseHg\extensions
  2. Open your mecurial.ini settings file located at %USERPROFILE%\mercurial.ini
  3. Under the [extensions] add property with path to commitsigs.py

    commitsigs = C:\Program Files\TortoiseHg\extensions\commitsigs.py

  4. Add a [commitsigs] section with the following properties:

    [commitsigs]
    scheme = gnupg
    gnupg.path = C:\Program Files (x86)\GNU\GnuPG\gpg2.exe
    gnupg.flags = --local-user <emailaddress>

    where <emailaddress> is the value you provided when creating your private key.

Your mercurial.ini should look something like this:

Testing

Check the plugin is found by mercurial by entering 'hg help commitsigs'. You should see the help output:

Signing commits

When you commit, you will be prompted for your the passphrase for the key you created. If you have pgp-agent.exe running in the background, this will happen once per windows session:

Verifying signatures

Check the changeset signature by issuing 'hg verifysigs':

The signature is part of the changeset and can be visible via 'hg log --debug':

Script to strip strong names from a project's references.

In my (never-ending) quest for true continous integrations nirvana, I find that version numbers in the a projects reference...

<Reference Include="System.Reactive, Version=1.0.2787.104, Culture=neutral, etc">
	<HintPath>..\..\libs\RxFramework\System.Reactive.dll</HintPath>
</Reference>

... sometimes obstruct me. Especially when dropping in a new version and attempting to build from a script.

This script below will recursively go through all the .csproj files from a base directory and strip out the strong name above, leaving:

<Reference Include="System.Reactive">
	<HintPath>..\..\libs\RxFramework\System.Reactive.dll</HintPath>
</Reference>

Le script:

param(
	[string]$path = $(throw "path required.")
)

[Reflection.Assembly]::LoadWithPartialName("System.Xml.Linq") | Out-Null
$csprojs = Get-ChildItem . -Recurse | Where-Object {$_.Extension -match "csproj"} | Select-Object FullName
$ns = "http://schemas.microsoft.com/developer/msbuild/2003"
foreach($csproj in $csprojs){
	$projectModified = $false
	$path = $csproj.FullName
	$csprojXmlDoc = [System.Xml.Linq.XDocument]::Load($path)
	$csprojXmlDoc.Descendants("{$ns}Reference") | 
		Where-Object {$_.Attribute("Include").Value.Contains(",") } | 
		ForEach { 
			$index = $_.Attribute("Include").Value.IndexOf(',')
			$newAttributeValue =  $_.Attribute("Include").Value.SubString(0, $index)
			Write-Host ($path + ": " + $_.Attribute("Include").Value + " -> $newAttributeValue")
			$_.Attribute("Include").Value = $newAttributeValue
			$projectModified = $true
			}
	if($projectModified -eq $true){
		$csprojXmlDoc.Save($path)
	}
}

Inconsistent Code Style annoys me

Honestly, I don't care if you are a spaces kinda person, or a tabs kinda person, or your class members start with 'm_' / '_' / nothing at all. What ever you choose, just be consistant throughout your code base. It makes your code readable and approachable, especially for someone new to the project. And if you have multiple people commiting use StyleCop to enforce a consistent style. Consistency trumps style preference each and every time. It really helps stave off code rot too.

This test method is from the NuGet source:

The NuGet project is well layed out and very approachable. When contributing to OSS project, you will have a better chance of getting your pull request accepted if it matches the maintainers style. Here we have different casing style for locally declared variables and an unclear rule as to when one should use var or not. All in same method so was probably written by same person. This add a barrier, albeit a small one, to contributing.

Shipping Code Contracts Reference Assemblies

Problem

So say I have SolutionA that has ProjectA that outputs:

    bin\Release\ProjectA.dll
    bin\Release\CodeContracts\ProjectA.Contracts.dll

A post build step on our CI server copies this build output to a specific LocationA ( a build artifact folder to be specific)

Then we have SolutionB, that has ProjectB, that has a reference to ProjectA in LocationA. The static checker picks ProjectA.Contracts.dll just fine. All good.

The build output from SolutionB is:

    bin\Release\ProjectA.dll
    bin\Release\ProjectB.dll
    bin\Release\CodeContracts\ProjectB.Contracts.dll

Note, ProjectA.Contracts.dll is not part of the build output. Post build step copies this output to LocationB.

Now, we have a third solution, SolutionC, that has ProjectC, that has explicit dependency on ProjectB and implicit dependency on ProjectA. Both of which are in LocationB.

The problem here is that static checker, when analyzing ProjectC, has no access to ProjectA.Contracts.dll and so has warnings.

What I think I would like is to have ProjectB's output look like:

    bin\Release\ProjectA.dll
    bin\Release\ProjectB.dll
    bin\Release\CodeContracts\ProjectA.Contracts.dll <- include this in output
    bin\Release\CodeContracts\ProjectB.Contracts.dll

And correspondingly, I'd like ProjectC's output to look like:

    bin\Release\ProjectA.dll
    bin\Release\ProjectB.dll
    bin\Release\ProjectC.dll
    bin\Release\CodeContracts\ProjectA.Contracts.dll
    bin\Release\CodeContracts\ProjectB.Contracts.dll
    bin\Release\CodeContracts\ProjectC.Contracts.dll

etc.

Solution

Add an AfterTarget the Microsoft.CodeContracts.targets file (%ProgramFiles(x86)%\Microsoft\Contracts\MsBuild\v4.0  and \3.5) that will copy it's code contracts reference assemblies, if they exist, to the output directory of the project being compiled:

<!--=====================================================================
   Copy reference's Contract Reference Assembles that are marked as "CopyLocal"
  ======================================================================-->
 <Target
   Name="CopyReferenceCodeContractReferenceAssemblies"
	 Condition="'@(ReferenceCopyLocalPaths)' != ''"
	 AfterTargets="CodeContractReferenceAssembly">
  <ItemGroup>
    <CodeContractsReferenceAssembliesFiles
	   Include="@(ReferenceCopyLocalPaths->'%(RootDir)%(Directory)CodeContracts\%(Filename).Contracts%(Extension)')"
	   />
	  <CodeContractsReferenceAssembliesCopiedFiles/>
  </ItemGroup>
  <Copy
    SourceFiles="@(CodeContractsReferenceAssembliesFiles)"
    DestinationFolder="$(OutDir)CodeContracts"
    SkipUnchangedFiles="$(SkipCopyUnchangedFiles)"
    Retries="$(CopyRetryCount)"
    RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"
    Condition="'$(UseCommonOutputDirectory)' != 'true' And Exists('%(RootDir)%(Directory)%(Filename)%(Extension)')"
    >
	  <Output TaskParameter="CopiedFiles" ItemName="CodeContractsReferenceAssembliesCopiedFiles"/>
	</Copy>
	<ItemGroup>
    <FileWrites Include="@(CodeContractsReferenceAssembliesCopiedFiles)"/>
	</ItemGroup>
 </Target>

This will need to be done on your build server too.

We have been using this at my company for several months now without issues.

Original discussion. Thx to Mike Barnett for the suggestion.

A NuGet (for tools) / OpenWrap (for libs) hybrid Visual Studio solution template

I've created a solution template as a that may form the basis of reference solution to be used in my company. It's available to view here

Requirements & Design decisions:

  1. To be used in a corp environment.
  2. No packages / wraps / dependencies / tools are committed to version control, except the bare minimum to bootstrap the build and/or restore dependencies.
  3. Packages / wraps / dependencies / tools may come from a variety of sources - nuget galley, local nuget repo, local openwrap repo.
  4. Anyone should be able to download the source and single click build the solution with no additional effort, friction or setup. (i.e. just run Build.ps1 )
  5. Easily managed via CI without addition setup and minimal coupling (i.e. .\Build.ps1 -buildNumber 2 )
  6. Sticking with released version of tools.
  7. I am using NuGet as my 'tools' manager. OpenWrap does not appear to be suitable for this purpose.
  8. OpenWrap seems to want to be responsible for compiling, running tests and creating the package. I already have psake for that. In my mind, creating a packages is a post-build step. Anyway, build: none to the rescue. 
  9. A psake build task sets the version number (using a build number if supplied) and keeps SharedAssemblyInfo.cs and the version file in sync.
  10. We use code contracts and the contract assemblies must be shipped in the wrap.

Some of the things I don't like:

  1. There is a bit too much going on in the root of the project, but at least Build.ps1 is at the top.
  2. Would rather not have to commit warps\openwrap-1.0.0.53270092.wrap to  support the bootstrap.
  3. ... and I'd like to add wraps/* to .hgignore.
  4. Can't isolate XUnit to only the DH.SolutionTemplate.Tests project (I know something is in the pipeline for this).
  5. Explicitly defining what goes into the resulting wrap may be a place where mistakes can easily happen. Not sure how to mitigate that.
  6. Having to use ;-shellInstall none; everywhere. I think o.exe default behaviour should be 'do nothing' and installation is an explicit step. I don't know enough of OpenWraps's architecture or intention to hold that belief firmly though :)
  7. In RestoreDependencies.ps1 I am deliberately adding the NuGet gallery remote source. This will add it permanently to the users remote list which I'd prefer not to affect. (I know there is something in OpenWrap's pipeline for this).

So, if there are any places this could be improved, let me know.

Using OpenWrap without having to install it

Well shortly after my last post on getting OpenWrap to work with TeamCity I was told that one can just include o.exe with your solution and use the switch

 

o -shellInstall none [rest of args]

 

instead.

This made me face-palm :)

In my defence, this switch is not shown via 'o', or 'o get-help'. Nor is it easily discoverable via the documentation / wiki when following links from openwrap.org. I also thought that because the default action of 'o' is to prompt you to install, I thought one had to.

And with that, I am of the opinion that 'o''s default action shouldn't prompt to install, and that installing should instead be an explicit switch ( '-install' ).