views:

128

answers:

2

I am new to NHibernate and I am having trouble mapping the following relationships within this class.

public class Category : IAuditable
{
    public virtual int Id { get; set; }
    public virtual string Name{ get; set; }
    public virtual Category ParentCategory { get; set; }
    public virtual IList<Category> SubCategories { get; set; }

     public Category()
    {
        this.Name = string.Empty;
        this.SubCategories = new List<Category>();
    }

}

Class Maps (although, these are practically guesses)

public class CategoryMap : ClassMap<Category>
{
    public CategoryMap()
    {
        Id(x => x.Id);
        Map(x => x.Name);

        References(x => x.ParentCategory)
            .Nullable()
            .Not.LazyLoad();

        HasMany(x => x.SubCategories)
            .Cascade.All();

    }
}

Each Category may have a parent category, some Categories have many subCategories, etc, etc I can get the Category to Save correctly (correct subcategories and parent category fk exist in the database) but when loading, it returns itself as the parent category.

I am using Fluent for the class mapping, but if someone could point me in the right direction for just plain NHibernate that would work as well.

+1  A: 

Ok so on the HasMany(x=>x.SubCategories) you need to add Inverse() to the call chain and also give it the column name which I'm assuming is "ParentCategoryId" given the mapping of the parent category, of course this depends on your conventions too.

If you were to post your table stucture I can give you a complete solution.

Chris Meek
Thanks Chris, I allow NHibernate to create the database structure for me without any modifications. It has created the columns Id, Name, ParentCategoryID, and CategoryID. The last two columns always match. I will try adding inverse and see the results.
Charlie Brown
Update: Inverse appears to have no effect, the result remains the same, although the SQL that is emmitted differs. The SQL on the insert side appears to be more correct using Inverse(), but the subcategories are still not loaded correctly. It looks like the SQL is not using the correct FK when selecting, which I'm guessing means it needs to know the column name as you suggest, although I do not know how to specify that with Fluent.
Charlie Brown
`HasMany(...).KeyColumn("ParentCategoryId")...`
Daniel Schilling
+1  A: 

By convention, Fluent NHibernate will look at "Category_Id" as foreign key column. It won't figure out your "ParentCategoryId" column. Unless you rename your self-referencing column to "Category_Id", you have to clarify the column name for both parent and child relationships.

For category without parent (absolute-parent category), whose reference column is null, it's reasonable to return itself as parent or null depending on how NHibernate handles it since you choose eager loading.

public class CategoryMap : ClassMap<Category> 
{ 
    public CategoryMap() 
    { 
        Id(x => x.Id); 
        Map(x => x.Name); 

        References(x => x.ParentCategory)
            .Column("ParentCategoryId")    // Many-To-One : parent
            .Nullable() 
            .Not.LazyLoad(); 

        HasMany(x => x.SubCategories) 
           .Cascade.All().Inverse().KeyColumn("ParentCategoryId");   //One-To-Many : chidren

    } 
} 
stoto
This solution is well written, so I'll give you a +1. Without specifying on the References side, nHibernate is looking for ParentCategory_Id, so I updated the HasMany to include the key column. Either way seems to work just fine, but I like the idea of telling it specifically as well.
Charlie Brown