Damian Hickey

Mostly software and .NET related. Mostly.

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.

Notify Property Weaver and Code Contracts

Notify Property Weaver is an AOP tool to convert automatic properties into properties that fire property changed events. I use code contracts, and since that also rewrites the outputted assemblies, I was wondering if there would be any conflict or issue.

Good news, my brief test (code below) tells me all is fine, though some more extensive usage would be required.

Couple of items I'd like to see in future versions of Notify Property Weaver:

  1. Dirty tracking. Which properties have changed. This would require the ability to 'load' a property with an initial value, without firing property changed event.
  2. Post-validation rules and IDataErrorInfo support.

Update: Simon Cropp, author of Notify Property Weaver, suggests a solution to validation and IDataErrorInfo using mixins.

namespace NotifyPropertyWeaverAndCodeContracts
{
	using System;
	using System.ComponentModel;
	using System.Diagnostics.Contracts;
	using NotifyPropertyWeaver;
	using Xunit;

	[ContractClass(typeof(FooContracts))]
	public interface IFoo
	{
		string Bar { get; set; }
	}

	[ContractClassFor(typeof(IFoo))]
	public class FooContracts : IFoo
	{
		public string Bar
		{
			get
			{
				return default(string);
			}
			set
			{
				Contract.Requires<ArgumentException>(!string.IsNullOrWhiteSpace(value));
			}
		}
	}

	public class Foo : IFoo, INotifyPropertyChanged
	{
		[NotifyProperty]
		public string Bar { get; set; }

		public event PropertyChangedEventHandler PropertyChanged;
	}

	public class FooTests
	{
		private readonly Foo _foo;

		public FooTests()
		{
			_foo = new Foo();
		}

		[Fact]
		public void When_set_Bar_to_null_Then_should_throw()
		{
			Assert.Throws<ArgumentException>(() => _foo.Bar = null);
		}

		[Fact]
		public void When_set_Bar_to_empty_Then_should_throw()
		{
			Assert.Throws<ArgumentException>(() => _foo.Bar = string.Empty);
		}

		[Fact]
		public void When_set_Bar_to_a_value_Then_should_raise_property_changed()
		{
			bool eventRaised = false;
			_foo.PropertyChanged += (sender, args) => eventRaised = args.PropertyName == "Bar";
			_foo.Bar = "a";
			Assert.True(eventRaised);
		}
	}
}