5

I have a simple Fluent NHibernate model with two related classes:

public class Applicant
    {
        public Applicant()
        {
            Tags = new List<Tag>();
        }

        public virtual int Id { get; set; }

        //other fields removed for sake of example

        public virtual IList<Tag> Tags { get; protected set; }

        public virtual void AddTag(Tag tag)
        {
            tag.Applicant = this;
            Tags.Add(tag);
        }
    }


public class Tag
{
    public virtual int Id { get; protected set; }
    public virtual string TagName { get; set; }

    public virtual Applicant Applicant { get; set; }
}

My fluent mapping is the following:

public class ApplicantMap : ClassMap<Applicant>
    {
        public ApplicantMap()
        {
            Id(x => x.Id);

            HasMany(x => x.Tags).Cascade.All();
        }
    }

    public class TagMap : ClassMap<Tag>
    {
        public TagMap()
        {
            Id(x => x.Id);
            Map(x => x.TagName);

            References(x => x.Applicant).Not.Nullable();
        }
    }

Whenever I try to update an applicant (inserting a new one works fine), it fails and I see the following SQL exception in the logs:

11:50:52.695 [6] DEBUG NHibernate.SQL - UPDATE [Tag] SET Applicant_id = null WHERE Applicant_id = @p0;@p0 = 37 [Type: Int32 (0)] 
11:50:52.699 [6] ERROR NHibernate.AdoNet.AbstractBatcher - Could not execute command: UPDATE [Tag] SET Applicant_id = null WHERE Applicant_id = @p0 System.Data.SqlClient.SqlException (0x80131904): Cannot insert the value NULL into column 'Applicant_id', table 'RecruitmentApp.dbo.Tag'; column does not allow nulls. UPDATE fails.

Why is NHibernate trying to update the tag table and set Applicant_id to null? I'm at a loss on this one.

arknotts
  • 454
  • 3
  • 13

1 Answers1

13

Set Applicant.Tags to Inverse will instruct NHibernate to save Tags after the Applicant.

public class ApplicantMap : ClassMap<Applicant>
{
    public ApplicantMap()
    {
        Id(x => x.Id);

        HasMany(x => x.Tags).Cascade.All().Inverse();
    }
}

More detail:

Inverse (as opposed to .Not.Inverse()) means the other side of the relationship (in this case, each Tag) is responsible for maintaining the relationship. Therefore, NHibernate knows that the Applicant must be saved first so that Tag has a valid foreign key for its Applicant.

Rule of thumb: The entity containing the foreign key is usually the owner, so the other table should have Inverse

moribvndvs
  • 42,191
  • 11
  • 135
  • 149
  • Worked like a charm. Thanks! I'm still a little confused on what inverse means, but I'll do a little reading. I don't see it in the documentation though. – arknotts Jun 26 '12 at 19:36
  • Thanks alot for resolving the issue it worked without any issues. – Desmond Jan 16 '13 at 07:43
  • This is weird to me. Why is the tag the owner? The tags are tied to a specific Applicant. You create an applicant, then add tags to it. You don't create the tags, and associate them to an applicant? – Kevin Doyon Feb 15 '19 at 13:03