LINQ-to-NHibernate errors when using IQueryable to query component with Select, SelectMany, and OfType

StackOverflow https://stackoverflow.com/questions/19502433

Question

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();
        });
    }
}
Was it helpful?

Solution

As @Firo pointed out, NHibernate was having some difficulty with the components I'm using for my custom collections. If you look at the SQL in the question, you'll see that NHibernate failed to join to the Activities table and the Departments table to look for the associated responsible department.

I corrected this by switching over to LINQ query syntax and explicitly joining to the tables. I also avoided issues with the OfType (again, most likely component issues) by checking inline with is and casting with as:

var schedules = from schedule in session.Query<Schedule>()
                join activity in session.Query<ScheduleAcivity>() on schedule equals activity.Schedule
                join department in session.Query<FeedActivityDepartment>() on activity equals department.FeedActivity                        
                where (activity is FeedActivity && (activity as FeedActivity).ResponsibleDepartment.Manager == employee) 
                    || (department != null && department.Employee == employee)
                select schedule;

var exhibits = from exhibit in session.Query<Exhibit>()
               where schedules.Any(x => x == exhibit.AnimalSchedule)                             
               select exhibit;

Note: Sometimes NHibernate is finicky about the names of aliases in the LINQ queries. If you follow this pattern and are still experiencing errors, try changing the names of your aliases (I like to add "temp" in front of them -- from tempSchedule in ..., join tempActivity in ..., etc.).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top