views:

703

answers:

2

I've the following class:

public class Customer
{
    public virtual int CustomerID { get; protected set; }
    public virtual string AccountNumber { get; protected set; }
    public virtual string CustomerType { get; set; }
    public virtual int TerritoryID { get; set; }
    public virtual SalesTerritory Territory { get; protected set; }
}

And it is being mapped using Fluent NHibernate as follows:

    public class CustomerMapper : ClassMap<Customer>
{
    private const string schema = "Sales";
    public CustomerMapper()
    {
        SchemaIs(schema);
        Id(x => x.CustomerID);
        Map(x => x.CustomerType).WithLengthOf(1).Not.Nullable();
        Map(x => x.TerritoryID).Not.Nullable();

        Map(x => x.AccountNumber).ReadOnly()
            .Update(false)
            .Insert(false)
            .Generated(Generated.Insert);

        References<SalesTerritory>(x => x.Territory).TheColumnNameIs(ReflectionHelper.GetProperty<Customer>(x => x.TerritoryID).Name).LazyLoad().Update(false).Insert(false);
    }
}

The Update and Insert methods are just extension methods that set the update and insert attributes of a property in the generated mapping file.

This is how the resulting mapping file looks:

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-lazy="true" assembly="Model" namespace="Model" schema="Sales">
    <class name="Customer" table="`Customer`" xmlns="urn:nhibernate-mapping-2.2">
        <id name="CustomerID" column="CustomerID" type="Int32">
            <generator class="identity" />
        </id>
        <property name="AccountNumber" column="AccountNumber" insert="false" update="false" generated="insert" length="100" type="String">
            <column name="AccountNumber" />
        </property>
        <property name="TerritoryID" column="TerritoryID" not-null="true" type="Int32">
            <column name="TerritoryID" />
        </property>
        <property name="CustomerType" column="CustomerType" not-null="true" length="1" type="String">
            <column name="CustomerType" />
        </property>
        <many-to-one lazy="proxy" update="false" insert="false" name="Territory" column="TerritoryID" />
    </class>
</hibernate-mapping>

My problem is that when a save a new customer instance into the database, the customer instance have its Territory property as null.
How can I make this property to contain the correct value after saving into the database? I am looking at something like the generated attribute for properties which retrieve the correct value after the session is flushed.
Or is there something wrong with my Customer class?
Thanks in advance.

EDIT 1: The database that I'm using for this model is AdventureWorks (SQL2005), tables Sales.Customer and Sales.SalesTerritory.

A: 

I think your mapping is quite strange ... What is the relation of the territoryId property with the Territory property ? In the many-to-one mapping , where you map the Territory property, don't you have to specify the type of the Territory ? (SalesTerritory). You're mapping 2 properties to the same column, why ?

Frederik Gheysels
Well, my idea was to create model classes like the ones that the LINQ to SQL designer generates.<br/>For each reference to another table it generates a property which contains the id that points to the primery key of this table and other property which contains the entity itself.<br/>
Jaime Febres
Nhibernate doesn't work like that. Just use properties that point to other entities and NH will take care of the rest for you.
gcores
+1  A: 

A more sensible mapping would be like:

public class CustomerMapper : ClassMap<Customer>
{
    private const string schema = "Sales";
    public CustomerMapper()
    {
        SchemaIs(schema);
        Id(x => x.CustomerID);
        Map(x => x.CustomerType).WithLengthOf(1).Not.Nullable();
        Map(x => x.AccountNumber).ReadOnly()
           .Generated(Generated.Insert);
        References(x => x.Territory).LazyLoad();
    }
}

Some points:

  • This counts in being a mapped SalesTerritory class.
  • TerritoryID is not necessary.
  • If you use Insert(false) and Update(false) those properties won't be saved to the database. ReadOnly() does both Insert(false) and Update(false).
  • CostumerType could perfectly be an enum.

This is more or less the idea (air code).

gcores
I'm going to go with your suggestion, initially I was doing it that, seems to be the right one. Oh, and thanks for the tip about ReadOnly, wasn't aware of that +1.
Jaime Febres