Damian Hickey

Mostly software and .NET related. Mostly.

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);
		}
	}
}