0
votes

I have a one to many relationship between two entities in NHibernate:

public class Application
{
    public string TaskId { get; set; } // Foreign key reference
    public Task Task { get; set; }     // The relation/navigation property
}

The db.Web.Applications.Create method just calls the "SaveOrUpdate" method of the NHibernate session inside a transaction.

The relavant mappings:

internal class ApplicationMap : ClassMap<Application>
{
    public ApplicationMap() : base()
    {
        Schema(...);
        Table(...);

        CompositeId()
            .KeyProperty(app => app.UserId, "...")
            .KeyProperty(app => app.TaskId, "task_id")
            .KeyProperty(app => app.TransactionId, "...");

        // Relations

        References(app => app.Task, "task_id")
            .ForeignKey("taskid")
            .Unique()
            .Not.Insert()
            .Cascade.Persist();
    }
}

internal class TaskMap : ClassMap<Task>
{
    public TaskMap()
    {
        Schema(..);
        Table(...);

        Id(task => task.Id, "task_id");

        HasMany(task => task.Applications)
            .KeyColumn("task_id");
    }
}

When I wrote a test against a real database to create a new Application I found that the navigation property was not lazy loading after insert:

var app = new Application(...)
{
    TaskId = "..."
};

db.Web.Applications.Create(app);
db.SaveChanges();

var actual = db.Web.Applications.Find(app.UserId, app.TaskId, app.TransactionId);

// actual.Task is null

The mappings are working as I expect, but after inserting a new Application object, accessing the Task property returns null instead of lazy loading that entity from the database. Can this be done, and if so, how?

1

1 Answers

1
votes

I think your problem comes in with the way you've mapped the Application class and it's corresponding Task object relation. I think you should map it like this instead:

public class Application
{
    public Task Task { get; set; }     // The relation/navigation property
}

internal class ApplicationMap : ClassMap<Application>
{
    public ApplicationMap() : base()
    {
        Schema(...);
        Table(...);

        CompositeId()
            .KeyProperty(app => app.UserId, "...")
            .KeyReference(app => app.Task, "task_id")
            .KeyProperty(app => app.TransactionId, "...");
    }
}

internal class TaskMap : ClassMap<Task>
{
    public TaskMap()
    {
        Schema(..);
        Table(...);

        Id(task => task.Id, "task_id");

        HasMany(task => task.Applications)
            .KeyColumn("task_id");
    }
}

There is no need to map a specific id property for the foreign key relationships. I ALWAYS map the entity only. It's much easier this way. Then your code above for insertion would be this:

var app = new Application(...)
{
    Task = session.Load<Task>(taskId)
};

db.Web.Applications.Create(app);
db.SaveChanges();

var actual = db.Web.Applications.Find(app.UserId, app.TaskId, app.TransactionId);