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

This entry was posted on Saturday, June 27th, 2009 at 15:51 and is filed under NHibernate. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

Leave a Reply