OMG...why can't I get nhibernate to work!

I am about to give up on this pos…unless somebody here simply gives me the right code to fix my problem. I’m tired of running in circles, and scouring the internet with no progress at all.

Problem: not updating collections with FK’s.

Example:

Forum forum = new Forum(“Sample”);
category.AddForum(forum);
categoryRepository.Update(category); <- fails with transient object error for forum with no CategoryId.

Mapped right using References<Category> and HasMany<Forum>. Conventions are as follows.

Ref:

instance.Column(instance.Property.Name + “Id”);

HasMany:

instance.Key.Column(instance.EntityType.Name + “Id”);
instance.Cascade.All();
instance.Access.ReadOnlyPropertyThroughCamelCaseField(CamelCasePrefix.None);

Now, I’ve tried every possible setting for inverse, all the different cascades, on both sides. Nothing works. It just flat fails, telling me it can’t insert NULL for Forum.CategoryId (field, not entity prop).

So what do I do???

More info: on a hunch, I set dbo.Forums.CategoryId to allow nulls. It began working. Apparently it makes two calls. One to insert, then another to update the FK’s. Can this be corrected so that it completes this in one step? I really don’t like having it allow nulls.

I think my recent numerous posts have muddled people’s understanding of my current problem. I take the blame for that.

In hopes of correcting that, I beg of you both, please download this special “test” project I mocked up and give it a look see. It has only the elements required to compile and run. Of course, you don’t have the db, but you can generate one if you wish using SchemaExport and all that.

Anyway, look at the two entities, the two mapping files, the four conventions, and then lastly, the single controller. In it, I’ve commented two tests. The first passed, and did what it was supposed to. The second failed with the following:

cannot insert null into dbo.Forums [ inesrt into (sequence, title, description, moderated, enabled, visible) values (?, ?, ?, ?, ?, ?)]

Of course this tells me it isn’t even trying to insert the categoryId, unless that happens after the initial insert.

http://66.226.34.217/downloads (grab the FNHT Nhibernate Test file)

I am positive this is either a mapping issue, and that I am simply not doing it correctly, or I need to make the categoryId field allow nulls.

I’d love to know where I am going brain dead…

Ok, setting up an inverse relationship should work, though must say some of these limitations you’re experiencing are due to your design and implementation there of, not the framework (NH).

Some things you need to do.

  1. Figure out what your aggregate root is.

  2. Consider breaking your domain to maintain the above root and use your repository to do what it’s meant to do.
    i.e. GetForumsForCategory(int categoryId) and running a query instead. Relying on recursion, (especially recursion that can put you in an infinite loop!) in a large domain is never a good idea.

  3. Sit down and think what your relationships really are, not just blindly code what seems to be what you’re meant to do. If a category or forum can have more than one parent and can be the parent to multiple forums it’s not a ManyToOne (HasMany).

Al

Alright, I have an answer. I’ll post it here so anybody else coming across this issue will know.

First, from the documents at nhforge:
http://nhforge.org/doc/nh/en/index.html#collections-onetomany

Very Important Note: If the <key> column of a <one-to-many> association is declared NOT NULL, NHibernate may cause constraint violations when it creates or updates the association. To prevent this problem, you must use a bidirectional association with the many valued end (the set or bag) marked as inverse=“true”. See the discussion of bidirectional associations later in this chapter.

This was my problem. My table indeed declare the FK field as non-nullable.

Moving on to the suggested bi-directional relationship (what I was going for anyway)…

From an excerpt there:

Changes made only to the inverse end of the association are not persisted.

This means I MUST update both sides. Testing also bears this out.

So I changed my current AddForum method from:

public virtual void AddForum(Forum forum)
{
if (forum == null)
throw new Exception("Forum can not be null!");
if (!forums.Contains(forum))
forums.Add(forum);
}

To this:

public virtual void AddForum(Forum forum)
{
if (forum == null)
throw new Exception("Forum can not be null!");
if (!forums.Contains(forum))
{
forums.Add(forum);
forum.Category = this;
}
}

It works now, though I wonder if it’ll hold up with that ref set to internal set? Not sure I like having it public set like that.

Cheers.

HasManyToMany … interesting, I haven’t seen that one yet. Incidentally, I’m not mapping any Id’s myself. The mapping looks like this:

In CategoryMap.cs: HasMany<Forum>(x => x.Forums);

In ForumMap.cs: References<Category>(x => x.Category);

Anyway, on a more base level, I commented out the back ref to category in the forum entity, adjusted the mapping, and it’s still not working. If I add a forum to a category via AddForum, it just fails citing a null foreign key. Shouldn’t it be inferring the CategoryId from the category that I added the forum to?

OK Serena, as D said, NH is an Object Relation Mapping framework, it infers relationships from objects.

So first port of call, Ii you have mapped both CategoryId and your Category list, remove the references to CategoryId.

i.e.

if you have something like this;


Map(x => x.CategoryId);
HasManyToMany(x => x.Categories);

Remove the first line.

Thanks. I’ll try there. But in the meantime, it’s not a property. Trimmed down they look like this:

public class Category : Entity
{
private IList<Forum> forums = new List<Forum>();
public virtual IEnumerable<Forum> Forums { get { return forums; } }
public virtual void AddForum(Forum forum)
{
if (!forums.Contains(forum)) forums.Add(forum);
}
}

and

public class Forum : Entity
{
public virtual Category Category { get; protected set; }
}

minus all the other methods and properties (of which CategoryId is NOT one).

Any ideas D?

[EDIT] Just registered for that nh group and submitted this question there as well. Looks like a decent resource. Thanks.

Best place to post is: http://groups.google.com/group/nhusers?pli=1

But, if the CategoryId is a property, then you will have to set it manually. NHibernate deals with objects/entities, not Id’s, so it will not know that it should set the forum.CatetgoryId when you set the forum.Category property as it’s not supposed to.