# Friday, November 11, 2011

I’m a lazy developer. Being lazy does not mean I avoid to work. It means that I like to reflect things I am doing and optimize and atomize stuff to get more time on the valuable tasks. Code generation is a tool I tend to use quite regularly and T4 is at most my generator of choice.

Generated files can cause a lot of merging conflicts. So they are not to be checked into my source control system (currently my choice is HG/Mercurial).

For the build server this means the files do not exist when the repository is freshly checked out – they need to be generated during the build right before the compile happens.

With code generation I usually tend to use a pattern that is for example also used by ASP.NET MVC Views/Controller generation: If there is a local directory containing code generation templates, use it. Otherwise utilize the system wide templates.

MsBuild version 4.0 comes with a feature called property functions. This allows to place for instance a “find directories” inside a property group.

I use them to:

  • Allow to set a system wide template directory using the command line/the MsBuild API.
  • If no directory is set use a local directory.
  • If non of the above is applied set a fallback default.

Here is the XML snippet that shows how to use the pattern:

<?xml version="1.0" encoding="utf-8"?>
<Project 
    DefaultTargets="Generate" 
	ToolsVersion="4.0" 
	xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
	
	<PropertyGroup>
		<RootFolder>..\src</RootFolder>
		<CodeGeneratorDir 
			Condition=" '$(CodeGeneratorDir)' == '' " />
		<LocalCodeGeneratorDir>
			CodeGenerationTemplates
		</LocalCodeGeneratorDir>
		<CodeGeneratorDirFallback>
			C:\CodeGenerationTemplates
		</CodeGeneratorDirFallback>
	</PropertyGroup>
	
	<ItemGroup>
		<ModelFiles 
			Include="..\src\**\*.xdml" 
		/>
		<TemplateDirectory 
			Include="$(CodeGeneratorDir);" 
		/>
		<TemplateDirectory 
			Condition=" '$(CodeGeneratorDir)' == '' " 
			Include="$([System.IO.Directory]::GetDirectories(
				&quot;$(RootFolder)&quot;, 
				&quot;$(LocalCodeGeneratorDir)&quot;, 
				System.IO.SearchOption.AllDirectories))"
		/>
		<TemplateDirectory 
			Condition=" '@(TemplateDirectory)' == '' " 
			Include="$(CodeGeneratorDirFallback)" 
		/>
	</ItemGroup>

	<Target Name="Generate">
		<Message Text="@(TemplateDirectory)" />
		<Message Text="@(ModelFiles)" />
  </Target>
  
</Project>

Friday, November 11, 2011 10:08:27 PM (W. Europe Standard Time, UTC+01:00)  #    Disclaimer  |  Comments [1]  | 
# Sunday, November 06, 2011

Once in a while it happens that an exception is thrown like the following:

Could not load file or assembly 'devcoach.Core, Version=1.0.11308.1, Culture=neutral, PublicKeyToken=0313e76cb5077f22' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)

What did cause this exception to be fired?

Lets have a look at the following picture which illustrates the scenario.

image

The build order is defined by the dependencies. As we have separate modular component oriented development each project resides it its own repository and has its own nuget package.

How is Visual Studio handling references?

image

If we do not reference from GAC or another Project the target is always (if the assembly is strong names – and yes it should!) the Assembly.FullName as seen here:

<?xml version="1.0" encoding="utf-8"?>
<Project
ToolsVersion="4.0"
DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <ItemGroup>
      <Reference
Include="devcoach.Core, Version=1.0.11308.1, …">
      <HintPath>..\packages\…\devcoach.Core.dll</HintPath>
  </Reference>

We change the “Version Specific” setting in Visual Studio’s property windows manually.

image

And manipulate the result to be:

<?xml version="1.0" encoding="utf-8"?>
<Project
ToolsVersion="4.0"
DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <ItemGroup>
      <Reference Include="devcoach.Core, Version=1.0.11308.1,…">
      <VersionSpecific>False</VersionSpecific>
<HintPath>..\packages\…\devcoach.Core.dll</HintPath>
  </Reference>

Why does this matter?

1) Sometimes during development – especially when hunting a bug – it happens that I create a solution that crosses repository frontiers. This requires to switch from a nuget reference to a project reference. After work is done it is important that the project reference is rolled back to a nuget package reference. I created a small helper for that job called Reference Switcher. The rolled back reference now points to a newer version as functionality was added or a bug was removed.

2) Almost every time before I start working on a project I update the nuget package references. The updated reference may points to a newer version if functionality was added or a bug was removed.

3) In a web application project while updating a package visual studio realizes that the file is not there or not accessable (while beeing written) and falls back to the assembly that was copied to the bin directory during the last build – the prior version of course.

So each and every time a new version is introduced (while Visual Studio is running) it means to be clever and behaves as described. When the solution is commited to the source control server, pulled by the build server, packages get updated, build fails – NO nuget package is published. we have the issue in place.

A build task to the rescue

I wrote a msbuild task to solve the problems by modifying the project file. It switches references with version information to the form:

<?xml version="1.0" encoding="utf-8"?>
<Project
ToolsVersion="4.0"
DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <ItemGroup>
      <Reference Include="devcoach.Core">
      <HintPath>..\packages\…\devcoach.Core.dll</HintPath>
  </Reference>

Additionally, if the HintPath’s innerText points to an assembly placed inside the bin directory it tries to find the corresponding package and references the assembly from there – of course without version information.

Usually I prefer XmlReader and Writer but as we are in a non-performance-critical area I just dumped the memory-eating XmlDocument in.

using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Xml;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;

namespace devcoach.Tools.BuildTasks
{
	public class ModifyProjectFile : Task
	{
		[Required]
		public string[] ProjectFile { get; set; }

		public bool SetVersionSpecificationToFalse { get; set; }
		public bool PreferPackageReferences { get; set; }

		public override bool Execute()
		{
			var listener = new MsBuildLogTraceListener(Log);
			Trace.Listeners.Add(listener);

			try
			{
				foreach (var projectFile in ProjectFile)
				{
					var projectFilePath = Path.GetFullPath(projectFile);
					Trace.TraceInformation(
						string.Concat(
							"Processing file '",
							projectFilePath,
							"'..."));
					if (SetVersionSpecificationToFalse)
					{
						Trace.TraceInformation(
							"Set version specification to false.");
						RemoveVersionSpecification(projectFilePath);
					}
					if (PreferPackageReferences)
					{
						Trace.TraceInformation(
							"Prefer package references.");
						ReplaceBinReferences(projectFilePath);
					}
				}

				return true;
			}
			catch (Exception ex)
			{
				Log.LogErrorFromException(ex);
				return false;
			}
			finally
			{
				Trace.Listeners.Remove(listener);

			}
		}

		private void ReplaceBinReferences(string projectFile)
		{
			var projectDirectory = Path.GetDirectoryName(projectFile);
			var pdInfo = new DirectoryInfo(projectDirectory);
			var solutionDirInfo = pdInfo.Parent;

			var packagesDir = 
				Path.Combine(
					solutionDirInfo.FullName,
					"packages");

			if (!Directory.Exists(packagesDir))
			{
				Trace.TraceInformation("No packages directory found...");
				return;
			}

			var projectDoc = new XmlDocument();
			projectDoc.Load(projectFile);

			var xmlnsmgr = new XmlNamespaceManager(projectDoc.NameTable);
			xmlnsmgr.AddNamespace(
				"msbuild",
				"http://schemas.microsoft.com/developer/msbuild/2003");

			var hintPathNodes=
				projectDoc.SelectNodes(
					"/msbuild:Project/msbuild:ItemGroup/msbuild:Reference/msbuild:HintPath",
					xmlnsmgr);

			if (hintPathNodes == null)
			{
				Trace.TraceInformation("No references found...");
				return;
			}
			foreach (XmlNode hintPathNode in hintPathNodes)
			{
				var hintPath = hintPathNode.InnerText;

				var binIndex = hintPath.IndexOf("\\bin\\");
				var startsWithBin = 
					hintPath.StartsWith(".\\bin\\") || 
					hintPath.StartsWith("bin\\");
				if (binIndex > -1 || startsWithBin)
				{
					var referenceFile = Path.GetFileName(hintPath);

					var newReference = 
						Directory.EnumerateFiles(
							packagesDir,
							referenceFile,
							SearchOption.AllDirectories).FirstOrDefault();

					if (newReference == null)
					{
						Trace.TraceInformation(
							string.Concat(
								"No package found for '",
								hintPath,
								"'..."));
						return;
					}

					Trace.TraceInformation(
						string.Concat(
							"Patching hintpath to '",
							newReference,
							"'..."));

					hintPathNode.InnerText = newReference;

				}
			}
			projectDoc.Save(projectFile);
		}

		public void RemoveVersionSpecification(string projectFile)
		{
			var projectDoc = new XmlDocument();
			projectDoc.Load(projectFile);

			var xmlnsmgr = new XmlNamespaceManager(projectDoc.NameTable);
			xmlnsmgr.AddNamespace(
				"msbuild",
				"http://schemas.microsoft.com/developer/msbuild/2003");

			var referenceNodes =
				projectDoc.SelectNodes(
					"/msbuild:Project/msbuild:ItemGroup/msbuild:Reference",
					xmlnsmgr);
			if (referenceNodes == null)
			{
				Trace.TraceInformation("No references found...");
				return;
			}
			foreach (XmlNode referenceNode in referenceNodes)
			{
				var include = referenceNode.Attributes["Include"].Value;

				var commaIndex = include.IndexOf(",");
				if (commaIndex > -1)
				{
					var reference = include.Substring(0, commaIndex);
					
					Trace.TraceInformation(
						string.Concat(
							"Unversion reference to '",
							reference,
							"'..."));
					referenceNode.Attributes["Include"].Value = reference;

				}
			}
			projectDoc.Save(projectFile);
		}

	}
}

The resulting manifest shows, that the Assembly is nonetheless references with a specific version:

image

Sunday, November 06, 2011 8:10:37 PM (W. Europe Standard Time, UTC+01:00)  #    Disclaimer  |  Comments [0]  | 
# Monday, October 03, 2011

The good thing about JavaScript is that its network/ajax calls are asynchronous. The bad thing about JavaScript is that its network/ajax calls are asynchronous.

It’s bad because nested calls increase complexity. Look at the following sample methods:

function f1(callback) { console.log('f1'); if (callback != null) { console.log('hasCallback'); callback(); } } function f2(callback) { console.log('f2'); if (callback != null) { console.log('hasCallback'); callback(); } } function f3(callback) { console.log('f3'); if (callback != null) { console.log('hasCallback'); callback(); } }

The calls should be executed one after another.

f3( function(){ return f2( function() { return f3(); } ); } );

Reading or even worst writing this makes me not happy. I don’t like the explicit nesting. It makes the code feeling complicate. It often don’t fits in the frame of 80 chars per line Sad smile.

Today I’ve put together a small helper class that collects calls and executes them by chaining them as callbacks.

var devcoach = devcoach || {}; devcoach.CallChain = function () { var cs = []; this.add = function (call) { cs.push(call); }; this.execute = function () { var wrap = function (call, callback) { return function () { call(callback); }; }; for (var i = cs.length-1; i > -1; i--) { cs[i] = wrap( cs[i], i < cs.length - 1 ? cs[i + 1] : null); } cs[0](); }; };

This few lines now let you write a more clear and easy to read calls automatically being nested calling the follower as callback.

var cc = new devcoach.CallChain(); cc.add(f1); cc.add(f2); cc.add(f3); cc.execute();
Monday, October 03, 2011 5:11:20 PM (W. Europe Daylight Time, UTC+02:00)  #    Disclaimer  |  Comments [0]  | 
# Wednesday, August 24, 2011

It’s community summer again. The same procedure as every year. In the middle there is some refreshing ICE.

The ICE-Conference in Lingen is something special to me because it is an admin event. I’m a developer. Maybe It’s caused by my roots – yeh, I was an admin once. Maybe caused by the fact that I tend to build bridges between admins and devs and build my developments aware of infrastructure.

image

This year I spoke about professional development on the client stack. I explained Javascripts OOP concepts from the .NET development perspective. Introduced JQuery and explained how it’s plugin model works. 

The session was rated along with others as 3rd bets of the conference – Thanks for attending and voting!

image

As always the networking with the *admin guys* was as great as the after-ice-party. I really enjoyed to meet up with a lot of people I’ve haven’t seen for a while.

Last but not least: Many thanks to Nicki Wruck and the ICE-Team for this great event.

Wednesday, August 24, 2011 12:46:25 PM (W. Europe Daylight Time, UTC+02:00)  #    Disclaimer  |  Comments [1]  | 
# Tuesday, August 16, 2011

image

Maybe something x64? Maybe something faster?

Who knows? For now I’m just dreaming…

Tuesday, August 16, 2011 5:30:59 PM (W. Europe Daylight Time, UTC+02:00)  #    Disclaimer  |  Comments [0]  | 
# Thursday, August 04, 2011

Since the year 2005 the non-commercial organization JustCommunity e.V. organises once a year the NRW Conf community event. Over the time it has become one of the biggest community events in Germany.

This years conference will be on the 8th and 9th of September located in Wuppertal, Germany.

We will host over 20 speakers and sessions from Germany and Europe, and as last year there will be a workshop day upfront:

Last but not least let me throw in some buzzwords, before you head over to the registration: Windows Phone 7.5, Agile, SQL Server, ASP.NET MVC, REST, Windows Azure, Rails, SCRUM, Powershell, Sharepoint, Oracle, Kinect, MS Office, Git, Performance-Analysis, TMG, UAG, FEP, FPE, FSSP, FPSMC, FIM, FOPE.

Click here to sign up…

ASP.NET | C# | Event | MVC | Wuppertal
Thursday, August 04, 2011 2:21:59 PM (W. Europe Daylight Time, UTC+02:00)  #    Disclaimer  |  Comments [4]  | 
# Wednesday, July 27, 2011

image

Wednesday, July 27, 2011 6:11:48 PM (W. Europe Daylight Time, UTC+02:00)  #    Disclaimer  |  Comments [0]  | 
# Wednesday, July 06, 2011

Especially in core libraries I tend to not reference System.Web.dll to keep the .NET Framework Client Profile as an option of compilation. I use the following code to figure out if the current running application is a web application

namespace devcoach.Extensions
{
    public static partial class AppDomainExtensions
    {
        public static bool IsWebApp(this AppDomain appDomain)
        {
            var configFile = (string)appDomain.GetData("APP_CONFIG_FILE");
            if (string.IsNullOrEmpty(configFile)) return false;
            return Path.GetFileNameWithoutExtension(configFile).Equals(
                "WEB", 
                StringComparison.OrdinalIgnoreCase);
        }
    }
}

Wednesday, July 06, 2011 2:14:48 AM (W. Europe Daylight Time, UTC+02:00)  #    Disclaimer  |  Comments [0]  | 
# Tuesday, July 05, 2011

image

Sad smile

Tuesday, July 05, 2011 10:26:36 PM (W. Europe Daylight Time, UTC+02:00)  #    Disclaimer  |  Comments [0]  | 
# Saturday, June 18, 2011

image

As Visual Studio keeps forgetting the settings that also affect validation and falls back to the *oh-so-90-style-2.1-css-version* just delete ALL other schemas.

 

Thanks to the *industry known* Sweat Swank for searching the registry key.

Saturday, June 18, 2011 12:58:21 PM (W. Europe Daylight Time, UTC+02:00)  #    Disclaimer  |  Comments [0]  |