Search code examples
c#nhibernatefluent-nhibernatenhibernate-mappingfluent-nhibernate-mapping

NHibernate unidirectional one-to-many relationship not saving foreign key


I'm new to NHibernate and I'm creating a simple scenario to test the framework functionalities.

I have to basic entities:

public class School : BaseEntity
{
    public virtual string Code { get; set; }
    public virtual string Name { get; set; }
}

public class Student : BaseEntity
{
    public virtual string Name { get; set; }
    public virtual string Surname { get; set; }
    public virtual string Email { get; set; }
    public virtual School School { get; set; }
}

inheriting from a simple base class:

public abstract class BaseEntity
{
    public virtual int Id { get; protected set; }
}

Than I map entities using FluentNhibernate this way:

return Fluently.Configure()
   .Database(MsSqlConfiguration.MsSql2012.ConnectionString(
        c => c.FromConnectionStringWithKey("DataModel")))
   .Mappings(m => m.AutoMappings
       .Add(AutoMap.AssemblyOf<BaseEntity>()
       .Where(t => t.Namespace == "MyApp.Models"))
       .IgnoreBase<BaseEntity>()
       .Override<User>(map =>
       {
           map.Table("Users");
           map.HasOne<School>(u => u.School).ForeignKey("SchoolId");
       })
       .Override<School>(map =>
       {
           map.Table("Schools");
       })
   ))
   .BuildSessionFactory();

My test code is very simple:

using (var transaction = DbSession.BeginTransaction())
{
    Student u1 = DbSession.Get<Student>("user-id");
    School s1 = DbSession.Get<School>("school-id");

    u1.School = s1; // updating the associated school

    DbSession.SaveOrUpdate(u1);

    transaction.Commit(); // !!! the foreign key is not updated
}

Checkign the Students table, the row is not updated with the new school id.

So, what's wrong in my code? Is there something incorrect (or missing) in my mappings?


Solution

  • A Student belonging to the School is a many-to-one relationship.

    5.1.11. many-to-one

    An ordinary association to another persistent class is declared using a many-to-one element. The relational model is a many-to-one association. (It's really just an object reference.)

    Its fluent version is .References()

    References / many-to-one

    References is for creating many-to-one relationships between two entities, and is applied on the "many side." You're referencing a single other entity, so you use the References method. #HasMany / one-to-many is the "other side" of the References relationship, and gets applied on the "one side."

    Let's map a relationship between a book and its author.

    public class Book
    {
      public Author Author { get; set; }
    }
    
    public class Author
    {
      public IList<Book> Books { get; set; }
    }
    

    In domain terms, we have an Author which may be associated with any number of Books, and Books, each of which can be associated with a single Author.

    In database terms, we'd have a book table with a foreign key column referencing the primary key of an author table.

    To create the references relationship in your Book #ClassMap, add the following call in the BookMap constructor:

    References(x => x.Author);
    

    Other words, if we need the many-to-one relationship to be mapped with fluent, we cannot use .HasOne() but .References()

    //map.HasOne<School>(u => u.School).ForeignKey("SchoolId");
    map.References(u => u.School, "SchoolId");
    

    To get full overview of the .References() API, read the second half of this article (the first half is bout mapping by code, the second is comparison with fluent):

    mapping by code - Many-to-One by Adam Bar

    Note - what is the .HasOne() (the one-to-one) scenario issues could be found here