views:

538

answers:

2

Using a Join and Component together in Fluent NHibernate Mapping throws "Could not find a getter for property exception". This is my C# code

using FluentNHibernate.Mapping;

namespace FnhTest {
    public class CustomerMap : ClassMap<Customer> {
        public CustomerMap() {
            Id(x => x.Id).GeneratedBy.Identity();
            Map(x => x.Name);

            Join("BillingInfo", m =>
                               {
                                   m.KeyColumn("CustomerId");
                                   m.Component(x => x.BillingInfo, c =>
                                                                   {
                                                                       c.Map(y => y.AccountNumber);
                                                                       c.Map(y => y.Address);
                                                                   });
                               });
        }
    }

    public class BillingInfo {
        public virtual string AccountNumber { get; set; }
        public virtual string Address { get; set; }
    }

    public class Customer {
        public virtual int Id { get; set; }
        public virtual string Name { get; set; }

        public virtual BillingInfo BillingInfo { get; set; }
    }
}

And this is my Database structure =>

Customers:
  Id (int)
  Name (varchar 50)
BillingInfo:
  Id (int)
  AccountNumber (varchar 50)
  Address (varchar 50)
  CustomerId (int) (Foriegn Key to the Customers Id)

Fluent NHibernate generates the right mapping for this setup, but for some reason it throws up an error. Given below are the mapping and the error

Mapping:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
  <class xmlns="urn:nhibernate-mapping-2.2" name="FnhTest.Customer, FnhTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="`Customer`">
    <id name="Id" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Id" />
      <generator class="identity" />
    </id>
    <property name="Name" type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Name" />
    </property>
    <join table="BillingInfo">
      <key>
        <column name="CustomerId" />
      </key>
      <component name="BillingInfo" insert="true" update="true" optimistic-lock="true">
        <property name="AccountNumber" type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
          <column name="AccountNumber" />
        </property>
        <property name="Address" type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
          <column name="Address" />
        </property>
      </component>
    </join>
  </class>
</hibernate-mapping>

Error:

TestCase 'M:FnhTest.Program.Main(System.String[])'
failed: An invalid or incomplete configuration was used while creating a SessionFactory. Check PotentialReasons collection, and InnerException for more detail.

  * Database was not configured through Database method.

    FluentNHibernate.Cfg.FluentConfigurationException: An invalid or incomplete configuration was used while creating a SessionFactory. Check PotentialReasons collection, and InnerException for more detail.

      * Database was not configured through Database method.
     ---> NHibernate.PropertyNotFoundException: Could not find a getter for property 'AccountNumber' in class 'FnhTest.Customer'
    at NHibernate.Properties.BasicPropertyAccessor.GetGetter(Type type, String propertyName)
    at NHibernate.Tuple.Component.PocoComponentTuplizer.BuildGetter(Component component, Property prop)
    at NHibernate.Tuple.Component.AbstractComponentTuplizer..ctor(Component component)
    at NHibernate.Tuple.Component.PocoComponentTuplizer..ctor(Component component)
    at NHibernate.Tuple.Component.ComponentEntityModeToTuplizerMapping..ctor(Component component)
    at NHibernate.Tuple.Component.ComponentMetamodel..ctor(Component component)
    at NHibernate.Mapping.Component.BuildType()
    at NHibernate.Mapping.Component.get_Type()
    at NHibernate.Mapping.SimpleValue.IsValid(IMapping mapping)
    at NHibernate.Mapping.PersistentClass.Validate(IMapping mapping)
    at NHibernate.Mapping.RootClass.Validate(IMapping mapping)
    at NHibernate.Cfg.Configuration.Validate()
    at NHibernate.Cfg.Configuration.BuildSessionFactory()
    d:\Builds\FluentNH\src\FluentNHibernate\Cfg\FluentConfiguration.cs(93,0): at FluentNHibernate.Cfg.FluentConfiguration.BuildSessionFactory()
       --- End of inner exception stack trace ---
    d:\Builds\FluentNH\src\FluentNHibernate\Cfg\FluentConfiguration.cs(100,0): at FluentNHibernate.Cfg.FluentConfiguration.BuildSessionFactory()
    D:\repositories\core\playground\minhajuddin\FnhTest\FnhTest\Program.cs(8,0): at FnhTest.Program.Main(String[] args)

      * Database was not configured through Database method.


0 passed, 1 failed, 0 skipped, took 6.46 seconds (Ad hoc).

I've searched all over the web but was unable to find anything helpful, Any kind of help would be greatly appreciated :)

EDIT: Well, I didn't find a way to do this in Fluent NHibernate, I am using whatever Torkel has posted as an answer. But, that was not my intention. Anyway.

+2  A: 

BillingInfo looks to me not like a component but an entity.

If you map BillingInfo as an entity you can map it from Customer as a association.

<many-to-one name="BillingInfo" property-ref="CustomerId" cascade="none"/>

The property-ref is importand as you do not want to join on on the BillingInfo Id but on the CustomerId, this however requires you to add a CustomerId property to your BillingInfo class.

Torkel
Yeah this can be done, But I would always need a `BillingInfo` object when I have a customer, I know this can be done with a `one to one` mapping but I prefer this way, because it automatically does an `outer join` in this case
Khaja Minhajuddin
Well it isn't going to work unless you design it properly. Torkel's advice is sound.
James Gregory
I agree with whatever Torkel is saying, but I was just wondering why it's not possible in Fluent NHibernate, when it is possible to do the same in NHibernate.
Khaja Minhajuddin
A: 

Forgive me if this is a dumb observation, but don't you need an Id member in BillingInfo?

Tom Bushell
I do have an Id field in my BillingInfo class, but it's kind of redundant, because it's not populated by NHibernate
Khaja Minhajuddin