3

I'm creating a model for a website top-menu structure -

I have a MenuObject model:

public class MenuObject
{
    public virtual int Id { get; set; }
    public virtual string Title { get; set; }
    public virtual List<MenuObject> Children { get; set; }
}

and a mapping:

public mapMenu()
{
    Id(x => x.Id)
        .Not.Nullable();
    Map(x => x.Title)
        .Not.Nullable();
    HasMany<MenuObject>(x => x.Children)
         .AsList();
}

Basically i want to be able to create a "Top Level" Menu item then add some child items to it - in database terms, there should be a ParentId field that contains the ID of the parent Menu Item (if any - this could be null)

I'm struggling to get my head around how this should be defined in my object model. Also once i hav this configured, how would i go about saving children? Would it be something like

public void InsertChild(MenuObject parent, MenuObject child)
{
    parent.Children.add(child)
    session.SAve(parent)
    .....
}

or would I have to save the child independantly and link it to the parent explicitly?

Edit *****

Thanks - so now i have the following in my model:

        public virtual MenuObject Parent { get; set; }
        public virtual List<MenuObject> Children { get; set; }

and this in the mapping:

 HasMany(x => x.Children)
       .AsList()
       .Inverse()
       .Cascade.All()
       .KeyColumn("ParentId");

        References(x => x.Parent, "ParentId");

I can now add children to parent itms in the following way:

oChild.Parent = oParent;
session.SaveOrUpdate(oParent);
session.Save(oChild);
transaction.Commit();

I think i'm onto a winner! Is that the best way to do it? THanks gdoron

1 Answers1

3

or would i have to save the child independantly and link it to the parent explicitly?

Anyway you have to "link' it to it's parent, If don't want to get exceptions...
You will have to save the child independently only if you don't specify Cascade.Save\All() in the mapping:

HasMany<MenuObject>(x => x.Children)
    .AsList().Inverse.Cascade.All(); // Inverse will increase performance.

You have to add a Parent property to connect the "child" to it's "Parent".
It's mapping is:

References(x => x.Parent);       

P.S.
You don't have to write Not.Nullable on Id, It's the defaults.

gdoron
  • 147,333
  • 58
  • 291
  • 367
  • I have it saving now however it doesn't work if i don't save both Parent and Child - can you see the problem? In addition, do i have to perform a manual process to load a Parent Object with a collection of children? It seems the object won't load current using oMenuObjects = session .CreateCriteria(typeof(MenuObject)) .Add(Expression.Eq("Active", active)) .List(); – user1162591 Jan 21 '12 at 21:24
  • @user1162591. It looks like you save it in the right way. So if it doesn't work without saving the child, so save it. What is **Active** in your `CreateCriteria`?, What exactly do you want to achieve? – gdoron Jan 21 '12 at 23:50
  • My criteria is as follows: `oMenuObjects = session .CreateCriteria(typeof(MenuObject)) .Add(Expression.Eq("Active", active)) .List(); return oMenuObjects;` This query was working when i only had singular properties - now that i have a collection property (Children), the query doesn't work. Do i need to explicitly fill the collection in the query? – user1162591 Jan 22 '12 at 13:47
  • @user1162591. But you don't have _Active_ in the _MenuObject_ entity. ? – gdoron Jan 22 '12 at 13:50
  • I do, i've just shown you a selection of the fields to illustrate my point. Are they any examples of querying objects that contain collections? – user1162591 Jan 22 '12 at 15:30
  • 1
    @user1162591. `Query` is much easier then `Criteria` `session.Query().Where(x => x.Active == active).Select(x => x.Children).ToList();` More in **Ayende** blog [post](http://ayende.com/blog/4023/nhibernate-queries-examples) – gdoron Jan 22 '12 at 16:45