I found a repeatable scenario where Fetch Join isn't working. I don't
know NHibernate well enough to troubleshoot exactly why it's
happening, but I was able to create a test in NHibernate.Tests that
triggers the issue. Here's how I'm able to repeat it:
Say you you have 3 entities. A root entity Policy has a 1-M to Tasks,
which has a M-1 to TeamMember.
class Policy
int Id
ISet<Task> Tasks
class Task
int Id
TeamMember TeamMember
Issue this query:
Policy p = s.CreateQuery("SELECT p FROM Policy p " +
"LEFT JOIN FETCH p.Tasks t " +
"WHERE p.Id = :id")
.SetParameter("id", p2.Id)
.FutureValue<Policy>().Value;
Then this one:
IEnumerable<Task> tasks = s.CreateQuery("SELECT t FROM Task t " +
"INNER JOIN FETCH t.TeamMember ")
.Future<Task>();
And this check will fail:
for each (Task t in tasks)
Assert.IsTrue(NHibernateUtil.IsInitialized(t.TeamMember))
Not all TeamMember properties on the returned set of tasks will be
initialized. They'll be uninitialized proxies. Oddly enough, if I
comment out the first query, the 2nd one does exactly what I would
expect it to do.
Is there a reason this is happening, or did I run into an obscure bug?
Here's the method body of my failing test:
[Test]
public void TestDansWeirdScenario()
{
Policy p1, p2;
Task t1, t2;
TeamMember tm1, tm2;
using (ISession s = sessions.OpenSession())
using (ITransaction t = s.BeginTransaction())
{
tm1 = new TeamMember() { Name = "Joe" };
tm2 = new TeamMember() { Name = "Bill" };
s.Save(tm1);
s.Save(tm2);
p1 = new Policy() { PolicyNumber = 5 };
p1.Tasks.Add(new Task() { Policy = p1, TaskName = "Call
Customer",
TeamMember = tm1 });
p2 = new Policy() { PolicyNumber = 5 };
p1.Tasks.Add(new Task() { Policy = p2, TaskName = "Send
Invoice",
TeamMember = tm2 });
p1.Tasks.Add(new Task() { Policy = p2, TaskName = "Something
Else",
TeamMember = tm2 });
s.Save(p1);
s.Save(p2);
t.Commit();
}
using (ISession s = sessions.OpenSession())
using (ITransaction t = s.BeginTransaction())
{
Policy reloadP2 = s.CreateQuery("SELECT p FROM Policy p " +
"LEFT JOIN FETCH p.Tasks t " +
"WHERE p.Id = :id")
.SetParameter("id", p2.Id)
.FutureValue<Policy>().Value;
IEnumerable<Task> tasks = s.CreateQuery("SELECT t FROM Task t "
+
"INNER JOIN FETCH t.TeamMember ")
.Future<Task>();
foreach (Task tsk in tasks)
{
Assert.IsTrue(NHibernateUtil.IsInitialized(tsk.TeamMember));
}
}
}