使用 IQueryable 查询具有 Select、SelectMany 和 OfType 的组件时出现 LINQ to

本文关键字:组件 to LINQ OfType 查询 IQueryable Select SelectMany 使用 | 更新日期: 2023-09-27 18:36:21

出于这个问题的目的,假设我有一个有多个展品的动物园。每个展览都有自己的时间表,包括一系列活动,以及负责这些活动的部门和子部门列表。

我有一个相当大的 LINQ 查询,我正在使用它来获取任何附有员工的展品。当将查询与IEnumerablesession.Query<Exhibit>().AsEnumerable())一起使用时,它工作正常;但是,当我将查询切换到使用 IQueryable 时,NHibernate 崩溃了。

我已经将原始错误的初始来源确定为查询中的一个特定条件:

var filtered = session.Query<Exhibit>().Where(x => 
    x.AnimalSchedule.Activities
                    .OfType<FeedActivity>()
                    .Any(g => g.ResponsibleDepartment.Manager == employee)         
);

由于这个条件本身就很长,所以我继续逐步分解它:

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.

活动错误还给出了 NHibernate 尝试生成的 SQL:

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

如果您注意到,NHibernate 未能列出 SELECT 语句中的任何列。

我对这个查询的想法是错误的吗?如果这实际上是 NHibernate 中的一个 LINQ lambda 中的错误,是否有一些解决方法?


更新 - 请参阅下面的答案

看起来 NHibernate 在确定要加入哪个表以进行.SelectMany(x => x.Activities)时遇到了一些麻烦


作为参考,以下是所涉及的类的所有简化的 Fluent 映射:

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();
        });
    }
}

使用 IQueryable 查询具有 Select、SelectMany 和 OfType 的组件时出现 LINQ to

正如@Firo指出的那样,NHibernate在我用于自定义集合的组件方面遇到了一些困难。如果您查看问题中的 SQL,您会发现 NHibernate 未能联接到Activities表和Departments表以查找关联的负责部门。

我通过切换到 LINQ 查询语法并显式联接到表来更正此问题。我还通过检查内联is并使用as进行铸造来避免OfType问题(再次,很可能是组件问题):

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;

注意: 有时 NHibernate 对 LINQ 查询中的别名名称很挑剔。如果您遵循此模式并且仍然遇到错误,请尝试更改别名的名称(我喜欢在它们前面添加"temp"——from tempSchedule in ...join tempActivity in ...等)。