views:

142

answers:

1

Using NHibernate 2.0, NHibernate class attributes for mappings, ASP.NET MVC to create a message board type app.

I have 2 entities, Post and User. Post has an Owner property which is an instance of User. It also has a Replies property which is a collection of type ISet.

The idea is that one post can have one level of replies, the same as a SO post with comments. My MVC page use a partial view (of type Mvc.ViewUserControl) to display the top level (parent) posts. The ParentPost partial view in turn uses a ChildPost partial view (also of type Mvc.ViewUserControl) to display the replies (different display markup for the replies).

Everything works great, except that the Owner instances of type User are null in the Replies collection. They get loaded just fine in the parent collection.

In other words, at the parent level all properties are loaded correctly, including the owner of the post. In the Replies collection, the reply posts are loaded with all properties except for their owners. For what it's worth, Owner is the only class property of Post.

Can someone help me to figure out how to make NHibernate load the Owner instances in the Replies collection?

Here is the relevant mapping for Post:

[Class(Table="t_Posts",Lazy=false)]
public class Post : IPost
{
    [Id(Name = "PostId")]
    public virtual long PostId { get; set; }

    [Property(Column="OwnerID")]
    public virtual long OwnerId { get; set; }

    [Property(Column="DatePosted")]
    public virtual DateTime DatePosted { get; set; }

    [OneToOne(0,ForeignKey="OwnerId",Lazy=Laziness.False,ClassType=typeof(User))]
    public virtual IUser Owner { get; set; }

    [Property(Column="ParentID")]
    public virtual long ParentId { get; set; }

    [Set(0,Name="Replies",Inverse=true,Cascade="all-delete-orphan", Lazy=false)]
    [Key(1,Column="ParentId")]
    [OneToMany(2,ClassType=typeof(Post))]
    public virtual ISet<Post> Replies{ get; set; }
}

Here is the relevant mapping for User:

[Class(Lazy=false,Table="t_Users")]
public class User : IUser
{
    [Id(Name="UserId")]
    public virtual long UserId { get; set; }

    [Property(Column="LoginName")]
    public virtual string LoginName { get; set; }
}
+1  A: 

For Post.Owner, you should map it with many-to-one. My example in XML

<many-to-one name="Owner" lazy="false" column="OwnerId"/>

one-to-one mapping will assume the two entities have the same ID value. One more point to note is, ForeignKey in NH Mapping means the name of FK, not foreign key column.

A friendly note here, you don't need both OwnerId and Owner properties. All you need is just Owner property. You don't need ParentId property also, but make sure you set Inverse=false on Post.Replies

Full example:

public class Post
{
    public virtual long PostId { get; set; }
    public virtual DateTime DatePosted { get; set; }
    public virtual User Owner { get; set; }
    public virtual ISet<Post> Replies { get; set; }

    public Post()
    {
        Replies = new HashedSet<Post>();
    }
}

public class User
{
    public virtual long UserId { get; set; }
    public virtual string LoginName { get; set; }
}

Post's mapping

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
          namespace="Mfs.Core"
          assembly="Mfs.Core">
  <class name="Post" table="tPost" lazy="false">
    <id name="PostId">
      <generator class="hilo"></generator>
    </id>

    <property name="DatePosted" type="timestamp"/>

    <!--<one-to-one name="Owner" lazy="false" foreign-key="OwnerId"/>-->
    <many-to-one name="Owner" lazy="false" column="OwnerId"/>

    <set name="Replies" inverse="false" cascade="all-delete-orphan" lazy="false">
      <key column="ParentId"/>
      <one-to-many class="Post" />
    </set>
  </class>
</hibernate-mapping>

User's mapping

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
          namespace="Mfs.Core"
          assembly="Mfs.Core">
  <class name="User" table="tUser" lazy="false">
    <id name="UserId">
      <generator class="hilo"></generator>
    </id>
    <property name="LoginName"></property>
  </class>
</hibernate-mapping>
Canton
Ah, thank you! That was it. First time through with NHibernate, that was making me nuts. I think I will go back and read through the association chapters again :) BTW- thanks for the note about the OwnerID. I do know I didn't need that, it's just still in there from when I was just trying to get Post to load before going down the association path. Thanks again!!!
squillman