Tim Van Wassenhove

Passionate geek, interested in Technology. Proud father of two

27 Jun 2009

Beyond the basics: IPropertyAccessor

Consider the following classes: an abstract Account and a concrete SavingAccount

abstract class Account
{
	int Id { get; protected set; }
	int CustomerId { get; protected set; }
	abstract AccountType Type { get; }
}

class SavingAccount : Account, ISavingAccount
{
	private SavingAccount() { }
	public SavingAccount(int customerId) { CustomerId = customerId; }
	public override AccountType Type { get { return AccountType.SavingAccount; } }
}

And this is the schema on which we want to map these classes

screenshot of accounts schema

We define a Fluent NHibernate mapping as following:

public class AccountMap : ClassMap<account>
{
	public AccountMap()
	{
		WithTable("Accounts");
		Id(a => a.Id).ColumnName("account_id");
		Map(a => a.CustomerId).ColumnName("customer_id");
		Map(a => a.Type).ColumnName("account_type");
		SetAttribute("lazy", "false");

		JoinedSubClass<savingAccount>("saving\_account\_id", MapSavingAccount);
	}

	public void MapSavingAccount(JoinedSubClassPart<savingAccount> jscp)
	{
		jscp.WithTableName("SavingAccounts");
		jscp.SetAttribute("lazy", "false");
	}
}

As soon as we try to use this mapping we run into an “Could not find a setter for property ‘Type’ in class ‘Banking.Domain.CheckingAccount” exception. A quick look with reflector teaches us there are a couple of strategies, but none of them suits our needs.

screenshot of available property accessors in NHibernate assembly

Thus we decide to implement a custom PropertyAccessor as following

public class ReadOnlyProperty : IPropertyAccessor
{
	public bool CanAccessTroughReflectionOptimizer
	{
		get { return false; }
	}

	public IGetter GetGetter(Type theClass, string propertyName)
	{
		var basicPropertyAccessor = new BasicPropertyAccessor();
		var getter = basicPropertyAccessor.GetGetter(theClass, propertyName);
		return getter;
	}

	public ISetter GetSetter(Type theClass, string propertyName)
	{
		var setter = new NoOpSetter();
		return setter;
	}

	public class NoOpSetter : ISetter
	{
		public MethodInfo Method { get { return null; } }
		public string PropertyName { get { return null; } }
		public void Set(object target, object value) { }
	}
}

And now we can instruct NHibernate to use our custom PropertyAccessor as following:

public AccountMap()
{
	...
	Map(a => a.Type).Access.Using<readOnlyProperty>().ColumnName("account_type");
	...
}

A couple of searches later it appears that this problem had already been solved, but is not available in the version of NHibernate that comes with Fluent NHibernate. Oh well, we learned something new.