For the purposes of this question, let's say I have a zoo with multiple exhibits. Each exhibit has it's own schedule with a set of activities, as well as a department that is responsible for them and a list of sub-departments.
I have a fairly large LINQ query that I’m using to get any exhibits that has an employee attached to it. When using the query with an IEnumerable
(session.Query<Exhibit>().AsEnumerable()
) it works fine; however, when I switch the query over to use IQueryable
, NHibernate breaks down.
I’ve pinpointed the initial source of the original error to one specific condition in my query:
var filtered = session.Query<Exhibit>().Where(x =>
x.AnimalSchedule.Activities
.OfType<FeedActivity>()
.Any(g => g.ResponsibleDepartment.Manager == employee)
);
Since that condition is pretty long by itself, I went ahead and broke it out step-by-step:
var collection = session.Query<Exhibit>(); // works
var exhibitAnimalSchedules = collection.Select(x => x.AnimalSchedule); // works
var activities = exhibitAnimalSchedules.SelectMany(x => x.Activities); // could not execute query - Incorrect syntax near the keyword 'from'.
var feedActivities = activities.OfType<FeedActivity>(); // Could not find property nor field 'class' in class 'MyProject.Collections.ScheduleActivityCollection'
var filteredFeedActivities = feedActivities.Where(g => g.ResponsibleDepartment.Manager == employee); // Specified method is not supported.
The error on activities also gives the SQL that NHibernate tries to generate:
SELECT
FROM [dbo].[Exhibits] exhibit0_
INNER JOIN [dbo].[ExhibitAnimalSchedules] exhibitanima1_ ON exhibit0_.AnimalScheduleID = exhibitanima1_.ScheduleID
INNER JOIN [dbo].[Schedules] exhibitanima1_1_ ON exhibitanima1_.ScheduleID = exhibitanima1_1_.[ID]
WHERE exhibit0_.ZooID IS NULL
If you notice, NHibernate failed to list out any columns in the SELECT
statement.
Am I thinking the wrong way about this query? If this is actually a bug in NHibernate with one of the LINQ lambdas, is there some workaround?
UPDATE - See answer below
It looks like NHibernate is having some trouble figuring out which table to join to for the .SelectMany(x => x.Activities)
For reference, here are all of the simplified Fluent mappings for the classes involved:
public class ExhibitMap : ClassMap<Exhibit>
{
public ExhibitMap()
{
References(p => p.AnimalSchedule).Cascade.All();
}
}
public class ScheduleMap : ClassMap<Schedule>
{
public ScheduleMap()
{
Component(x => x.Activities, m => m {
var cfg = m.HasMany<ScheduleActivity>(Reveal.Member<ScheduleActivityCollection>("_innerList")).LazyLoad();
cfg.KeyColumn("ScheduleID").Inverse();
cfg.ForeignKeyConstraintName("FK_Schedule_Activities");
cfg.Cascade.AllDeleteOrphan();
});
}
}
public class ExhibitAnimalScheduleMap : SubclassMap<ExhibitAnimalSchedule>
{
public ExhibitAnimalScheduleMap()
{
References(x => x.Exhibit).Cascade.None();
}
}
public class ScheduleActivityMap : ClassMap<ScheduleActivity>
{
public ScheduleActivityMap()
{
References(x => x.Schedule).Cascade.None().Not.LazyLoad();
}
}
public class FeedActivityMap : SubclassMap<FeedActivity>
{
public FeedActivityMap()
{
this.References(x => x.ResponsibleDepartment).Cascade.All().Not.LazyLoad();
Component(x => x.Departments, m => m {
var cfg = m.HasMany<FeedActivityDepartment>(Reveal.Member<FeedActivityDepartmentCollection>("_innerList")).LazyLoad();
cfg.KeyColumn("ScheduleID").Inverse();
cfg.ForeignKeyConstraintName("FK_Schedule_Activities");
cfg.Cascade.AllDeleteOrphan();
});
}
}