Thursday, 30 October 2014

Using Process Monitor to solve a file copy failure during an automated build

The Problem

We use CruiseControl.Net (CCNet) for continuous integration and also for automated deployment of applications. The deployment processes usually include compiling an application, configuring it appropriately for a target environment (e.g. test, production, etc.), creating a deployment package such as a Zip archive, and then copying the package to the appropriate server(s) where it is installed.

However, we suddenly started getting failures when running these deployment builds. After checking the CCNet build logs we could see errors such as “Could not find a part of the path \\servername\deploymentfolder”.

Initial checks on the build server showed that the Universal Naming Convention (UNC) paths were actually valid. So what was going on?

The Solution

To find out what was going on I used Process Monitor to see if there were issues accessing the UNC paths.

Firstly, I grabbed the latest version of Process Monitor from Microsoft SysInternals. It’s free and does not need installation – it runs as a single executable. Then I copied Process Monitor (procmon.exe) to the machine in question and ran it. Once it has opened I did the following:

  1. Disabled event capture by clicking the magnifying glass icon so it has the red ‘X’ overlay.
  2. Clicked the eraser icon to clear all existing captured events.
  3. Made sure Process Monitor was only going to capture file system activity by only selecting the filing cabinet icon.

procmon

Then I needed to add a filter so I only saw events relating to the file/folder I was interested in:

  1. Clicked the filter icon (the Filter > Filter… menu option does the same thing).
  2. Once the Process Monitor Filter dialog opened, clicked the Reset button.
  3. Using the drop-down menus etc. created a filter that said “Path begins with \\servername\deploymentfolder then include”.
  4. Clicked the Add button to add the new filter.

procmon2

Finally I clicked the magnifying glass icon again to start event capture and forced one of the failing builds to run. Once the build completed I stopped capturing events again. This was the result:

SNAGHTML2522739c

So I could see that NAnt had been unable to copy the deployment file to the server because of a login failure. By double-clicking on an entry in the event list I could see more details about the issue including which account was being used.

Further digging identified the cause of the issue being related to the account being used to run the CCNet service and was easy to correct.

Thursday, 23 October 2014

Date operations with Noda Time

I was looking at a coding exercise recently that consisted of source code that needed refactoring. Embedded in part of the code was a check to see if someone was 21 or over. I thought this was a great chance to use the Noda Time library created by Jon Skeet to create nicely understandable code. Noda Time is described in the following terms:

“Noda Time is an alternative date and time API for .NET. It helps you to think about your data more clearly, and express operations on that data more precisely.” [1]

So, here’s a quick piece of playful code using Noda Time to check a person’s age in years. Firstly, let’s create a service interface.

using System;

namespace AgeService
{
    public interface IAgeService
    {
        bool IsOfAgeAtDate(DateTime dateOfTest, DateTime dateOfBirth, int expectedAgeInYears);
    }
}

Now let’s implement the interface.

using System;
using NodaTime;

namespace AgeService
{
    public class AgeService : IAgeService
    {
        public bool IsOfAgeAtDate(DateTime dateOfTest, DateTime dateOfBirth, int expectedAgeInYears)
        {
            var localDateOfTest = new LocalDate(dateOfTest.Year, dateOfTest.Month, dateOfTest.Day);
            var localDateOfBirth = new LocalDate(dateOfBirth.Year, dateOfBirth.Month, dateOfBirth.Day);

            Period age = Period.Between(localDateOfBirth, localDateOfTest, PeriodUnits.Years);

            return age.Years >= expectedAgeInYears;
        }
    }
}

A LocalDate is the date portion of a Noda Time LocalDateTime that has no concept of the time of day; it's just a date. A Period described as follows:

“A Period is a number of years, months, weeks, days, hours and so on, which can be added to (or subtracted from) a LocalDateTime, LocalDate or LocalTime. The amount of elapsed time represented by a Period isn't fixed: a period of "one month" is effectively longer when added to January 1st than when added to February 1st, because February is always shorter than January.” [2]

Finally here are a couple of simple tests to demonstrate it works.

using System;
using NUnit.Framework;

namespace AgeServiceTests
{
    [TestFixture]
    public class AgeServiceTests
    {
        [Test]
        public void IsOfAgeAtDate_WhenOfCorrectAge_ReturnsTrue()
        {
            // Arrange
            var ageService = new AgeService.AgeService();
            var dateOfTest = new DateTime(2014, 10, 23);
            var dateOfBirth = new DateTime(1993, 10, 23);

            // Act
            var result = ageService.IsOfAgeAtDate(dateOfTest, dateOfBirth, 21);

            // Assert
            Assert.That(result, Is.True);
        }

        [Test]
        public void IsOfAgeAtDate_WhenUnderAge_ReturnsFalse()
        {
            // Arrange
            var ageService = new AgeService.AgeService();
            var dateOfTest = new DateTime(2014, 10, 23);
            var dateOfBirth = new DateTime(1993, 10, 24);

            // Act
            var result = ageService.IsOfAgeAtDate(dateOfTest, dateOfBirth, 21);

            // Assert
            Assert.That(result, Is.False);
        }
    }
}

There’s lots more to Noda Time than that but it’s a start! Happy coding.

References

[1] http://nodatime.org

[2] http://nodatime.org/1.3.x/userguide/core-types.html

Thursday, 23 October 2014

Thursday, 14 August 2014

Using TeamCity to generate NuGet packages that reference other NuGet packages containing binaries for specific .Net versions

The Problem

I have been using the TeamCity continuous integration server to generate and publish NuGet packages automatically. The approach I have taken is based on that proposed by David Peden in this StackOverflow thread (see Option #1). Much appreciated, David.

This works pretty well until you try to generate a NuGet package that has dependencies on other NuGet packages, and in particular if a referenced package has different .Net builds in its lib folder. This problem can be illustrated in Visual Studio. If you change the .Net version of a project that has a NuGet reference to a package that contains specific .Net builds you might see an error.

 

image 

This is because the reference was created when the NuGet package was added to the project and has a path to the appropriate binaries in the NuGet package. Looking in the .csproj file for the references illustrates this further:

 

<ItemGroup>
    <Reference Include="Andy.French.Logging">
        <HintPath>..\packages\Andy.French.Logging.1.0.9\lib\net451\Andy.French.Logging.dll</HintPath>
    </Reference>
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Data" />
    <Reference Include="System.Xml" />
</ItemGroup>

 

Notice the logging framework reference path is to the net451 folder and therefore binaries built for that .Net version.

The same thing happened when I tried to generate the NuGet packages using David Peden’s approach without modification because it runs separate build steps for the different .Net versions. As a result in some cases the NuGet references were wrong for the step in question.

 

The Solution

For the time being I have come up with a somewhat hacky solution. It works for now but I am concerned it may not prove particularly maintainable. Time will tell.

The solution involves manually editing the .csproj file to include conditional references, something like this:

<ItemGroup>
    <Reference Condition="'$(TargetFrameworkVersion)' == 'v4.5.1'" Include="Andy.French.Logging, Version=1.0.1.0, Culture=neutral, processorArchitecture=MSIL">
        <SpecificVersion>False</SpecificVersion>
        <HintPath>..\packages\Andy.French.Logging.1.0.9\lib\net451\Andy.French.Logging.dll</HintPath>
    </Reference>
    <Reference Condition="'$(TargetFrameworkVersion)' == 'v4.5'" Include="Andy.French.Logging, Version=1.0.1.0, Culture=neutral, processorArchitecture=MSIL">
        <SpecificVersion>False</SpecificVersion>
        <HintPath>..\packages\Andy.French.Logging.1.0.9\lib\net45\Andy.French.Logging.dll</HintPath>
    </Reference>
    <Reference Condition="'$(TargetFrameworkVersion)' == 'v4.0'" Include="Andy.French.Logging, Version=1.0.1.0, Culture=neutral, processorArchitecture=MSIL">
    <SpecificVersion>False</SpecificVersion>
        <HintPath>..\packages\Andy.French.Logging.1.0.9\lib\net40\Andy.French.Logging.dll</HintPath>
    </Reference>
    <Reference Include="log4net, Version=1.2.13.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
        <SpecificVersion>False</SpecificVersion>
        <HintPath>..\packages\log4net.2.0.3\lib\net40-full\log4net.dll</HintPath>
    </Reference>
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Data" />
    <Reference Include="System.Xml" />
</ItemGroup>

When TeamCity runs the build for each target framework the appropriate reference will be used. For more complex sets of references the following approach can be used:

<Choose>
    <When Condition="'$(TargetFrameworkVersion)' == 'v4.5.1'">
        <ItemGroup>
            <Reference Include="Andy.French.Domain.Driven.Design, Version=1.0.6.0, Culture=neutral, processorArchitecture=MSIL">
              <SpecificVersion>False</SpecificVersion>
              <HintPath>..\packages\Andy.French.Domain.Driven.Design.1.0.6\lib\net451\Andy.French.Domain.Driven.Design.dll</HintPath>
            </Reference>
            <Reference Include="Andy.French.Repository, Version=1.0.1.0, Culture=neutral, processorArchitecture=MSIL">
              <SpecificVersion>False</SpecificVersion>
              <HintPath>..\packages\Andy.French.Repository.1.0.6\lib\net451\Andy.French.Repository.dll</HintPath>
            </Reference>
            <Reference Include="EntityFramework">
              <HintPath>..\packages\EntityFramework.6.1.1\lib\net45\EntityFramework.dll</HintPath>
            </Reference>
            <Reference Include="EntityFramework.SqlServer">
              <HintPath>..\packages\EntityFramework.6.1.1\lib\net45\EntityFramework.SqlServer.dll</HintPath>
            </Reference>
        </ItemGroup>
    </When>
    <When Condition="'$(TargetFrameworkVersion)' == 'v4.5'">
        <ItemGroup>
            <Reference Include="Andy.French.Domain.Driven.Design, Version=1.0.6.0, Culture=neutral, processorArchitecture=MSIL">
              <SpecificVersion>False</SpecificVersion>
              <HintPath>..\packages\Andy.French.Domain.Driven.Design.1.0.6\lib\net45\Andy.French.Domain.Driven.Design.dll</HintPath>
            </Reference>
            <Reference Include="Andy.French.Repository, Version=1.0.1.0, Culture=neutral, processorArchitecture=MSIL">
              <SpecificVersion>False</SpecificVersion>
              <HintPath>..\packages\Andy.French.Repository.1.0.6\lib\net45\Andy.French.Repository.dll</HintPath>
            </Reference>
            <Reference Include="EntityFramework">
              <HintPath>..\packages\EntityFramework.6.1.1\lib\net45\EntityFramework.dll</HintPath>
            </Reference>
            <Reference Include="EntityFramework.SqlServer">
              <HintPath>..\packages\EntityFramework.6.1.1\lib\net45\EntityFramework.SqlServer.dll</HintPath>
            </Reference>
        </ItemGroup>
    </When>
    <When Condition="'$(TargetFrameworkVersion)' == 'v4.0'">
        <ItemGroup>
            <Reference Include="Andy.French.Domain.Driven.Design, Version=1.0.6.0, Culture=neutral, processorArchitecture=MSIL">
              <SpecificVersion>False</SpecificVersion>
              <HintPath>..\packages\Andy.French.Domain.Driven.Design.1.0.6\lib\net40\Andy.French.Domain.Driven.Design.dll</HintPath>
            </Reference>
            <Reference Include="Andy.French.Repository, Version=1.0.1.0, Culture=neutral, processorArchitecture=MSIL">
              <SpecificVersion>False</SpecificVersion>
              <HintPath>..\packages\Andy.French.Repository.1.0.6\lib\net40\Andy.French.Repository.dll</HintPath>
            </Reference>
            <Reference Include="EntityFramework">
              <HintPath>..\packages\EntityFramework.6.1.1\lib\net40\EntityFramework.dll</HintPath>
            </Reference>
            <Reference Include="EntityFramework.SqlServer">
              <HintPath>..\packages\EntityFramework.6.1.1\lib\net40\EntityFramework.SqlServer.dll</HintPath>
            </Reference>
        </ItemGroup>
    </When>
</Choose>
<ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.ComponentModel.DataAnnotations" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Data" />
    <Reference Include="System.Xml" />
</ItemGroup>

 

It’s not perfect but it keeps me moving on for now.

Thursday, 14 August 2014

Saturday, 9 August 2014

ReSharper keyboard shortcuts stopped working

This post applies to ReSharper 8.2 running in Visual Studio Professional 2013 (12.0.30501.00 Update 2).

I’ve been installing a few Visual Studio extensions and ReSharper plugins recently and when I started Visual Studio this morning I found that the ReSharper keyboard shortcuts had stopped working. No problem though, it turned out to be an easy fix.

If you get this problem simply follow these steps:

  1. Go to ReSharper > Options.
  2. Locate Keyboard and Menus under the Environment section.
  3. I choose the Visual Studio keyboard scheme but you choose what’s appropriate to you.
  4. Click the Apply Scheme button.

That should be it. You may be prompted by ReSharper the first time you use certain ReSharper shortcuts to confirm what you want to do but this process has reset the ReSharper keyboard scheme.

 

image

Sunday, 20 July 2014

Null-coalescing operator in C#

It feels like I’ve been using C# forever. When you’ve been doing something for a long time it’s easy to fall into habits and miss or forget about techniques that could make your life a little easier so it doesn’t hurt to remind ourselves of things from time to time.

I have used many DI containers like Spring.Net, Unity, NInject etc. but I recently started using StructureMap for the first time. I added it to an MVC project using NuGet and when I looked at the code that had been added to my project I saw code like this:

protected override IEnumerable<object> DoGetAllInstances(Type serviceType) 
{
	return (this.CurrentNestedContainer ?? this.Container).GetAllInstances(serviceType).Cast<object>();
}

It really struck me that I couldn’t remember the last time I used the null-coalescing operator (??). Some quick revision:

“The ?? operator is called the null-coalescing operator. It returns the left-hand operand if the operand is not null; otherwise it returns the right hand operand.” [1]

The C# Reference goes on to describe more features of the ?? operator:

  • You can use the ?? operator’s syntactic expressiveness to return an appropriate value when the left operand has a nullable type whose value is null.
  • If you try to assign a nullable value type to a non-nullable value type without using the ?? operator, you will generate a compile-time error.
  • If you use a cast, and the nullable value type is currently undefined, an InvalidOperationException exception will be thrown.
  • The result of a ?? operator is not considered to be a constant even if both its arguments are constants.

So, by way of example, the following statements are all equivalent:

return nullableValue ?? someOtherValue;

return nullableValue != null ? nullableValue : someOtherValue;

if(nullableValue != null)
{
    return nullableValue;
}
else
{
    return someOtherValue;
}

References

Sunday, 20 July 2014

Sunday, 13 July 2014

Configuring ELMAH to use SQL Server

ELMAH (Error Logging Modules and Handlers for ASP.NET) is a great little project but the documentation could be improved. One thing I like to do is to get ELMAH logging to a SQL Server database pretty much as soon as it’s integrated into a project but the documentation is a bit scant. Here’s how you do it, in this case for an MVC application.

At the time of writing the core ELMAH library is in version 1.2.1 and the Elmah.MVC package is in version 2.1.1.

Install ELMAH

In your MVC application Manage NuGet Packages… and search for ‘elmah’. Install the Elmah.MVC package which will also install the ELMAH core library as a dependency.

image

Create the ELMAH database

Hop over to the ELMAH site and locate the Microsoft SQL Server DDL script on the downloads page. Download the DDL script to your machine.

SNAGHTMLdc8946

Open SQL Server Management Studio and create a database. I called mine Elmah. Open the DDL script and run it against the new database. This will create the tables and stored procedures used by ELMAH.

SNAGHTMLe49de1

Create a SQL login which will be used by ELMAH to connect to the Elmah database from your MVC application. I use SQL Server authentication. You’ll probably want to cancel password policy enforcement etc. if you do the same.

SNAGHTMLe842dc

Create a new User in the Elmah database using the Elmah login. Give the user data reader and data writer roles.

SNAGHTMLea5188 

You will also need to grant execute permissions to the ELMAH stored procedures:

USE Elmah; 
GRANT EXECUTE ON OBJECT::ELMAH_GetErrorsXml
    TO Elmah;
GO 

GRANT EXECUTE ON OBJECT::ELMAH_GetErrorXml
    TO Elmah;
GO 

GRANT EXECUTE ON OBJECT::ELMAH_LogError
    TO Elmah;
GO 

Modify the ELMAH configuration in the MVC application

When you added ELMAH to your MVC application it will have created an elmah section in your Web.config. You will need to update Web.config to include a connection string for the Elmah database and then update the elmah section to use that connection string.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <!-- other configuration removed for clarity -->
  <connectionStrings>
    <add name="elmah" connectionString="server=localhost;database=;uid=Elmah;password=password;" />
  </connectionStrings>
  <!-- other configuration removed for clarity -->
  <elmah>
    <errorLog type="Elmah.SqlErrorLog, Elmah" connectionStringName="elmah" applicationName="YourApplicationName"/>
  </elmah>
</configuration>

That should be all there is to get started. Now you’ll probably want to secure ELMAH.

Adding bundling and minification to an empty MVC site

If you create an empty MVC application you’ll probably want to add features like bundling and minification. The process is very simple and starts with NuGet.

At the time of writing MVC is in version 5.

Get the NuGet optimization package

In Visual Studio Manage NuGet Packages…

image 

Search on the term ‘optimization’.

image

Install the Microsoft ASP.Net Web Optimization Framework. Notice that this has dependencies that will also be installed.

image 

Create your bundle configuration class

In the App_Start folder of your application create a new class called BundleConfig. Create a RegisterBundles(BundleCollection bundles) static method in the class which you will use to register your bundles. If you’re not familiar with bundles you’ll probably want to look-up ScriptBundle and StyleBundle from the System.Web.Optimization namespace to see how to do that.

Here’s an example that registers my CSS files:

namespace Mansio.Web.UI
{
    using System.Web.Optimization;

    /// <summary>
    /// This class handles bundle configuration.
    /// </summary>
    public class BundleConfig
    {
        /// <summary>
        /// Registers bundles with the application.
        /// </summary>
        /// <param name="bundles">The bundles to register.</param>
        public static void RegisterBundles(BundleCollection bundles)
        {
            bundles.Add(new StyleBundle("~/Content/css").Include("~/Content/*.css"));
        } 
    }
}

Update Global.asax

The final step is to call the bundle configuration from Global.asax by calling your static RegisterBundles method from Application_Start.

/// <summary>
/// Called when the application starts.
/// </summary>
protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);
}

That’s all there is to it.

See also