导致N+1的NHibernate查询覆盖列表

导致N+1的NHibernate查询覆盖列表,nhibernate,fluent-nhibernate,fluent-nhibernate-mapping,Nhibernate,Fluent Nhibernate,Fluent Nhibernate Mapping,在NHibernate中有一个看似古怪的N+1选择问题。我正在执行一个查询,其中我请求一组实体,其中一个链接属性为null。在这种情况下,我并不需要NHibernate返回链接属性,因为它只是为了选择正确的数据 第一个实体是预订窗口 public class BookingWindow : Entity<BookingWindow> { // Blah blah blah /// <summary> /// Gets or sets the bo

在NHibernate中有一个看似古怪的N+1选择问题。我正在执行一个查询,其中我请求一组实体,其中一个链接属性为null。在这种情况下,我并不需要NHibernate返回链接属性,因为它只是为了选择正确的数据

第一个实体是预订窗口

public class BookingWindow : Entity<BookingWindow>
{
    // Blah blah blah

    /// <summary>
    /// Gets or sets the booking order item.
    /// </summary>
    /// <value>
    /// The booking order item.
    /// </value>
    public virtual BookingWindowOrderItem BookingOrderItem { get; set; }
}
下面是相应的映射

   public BookingWindowMap()
    {
        this.Schema("Customer");
        this.Table("BookingWindows");
        this.Id(x => x.Id).GeneratedBy.Guid();
        this.Component(x => x.WindowPeriod, m =>
        {
            m.Map(x => x.Min, "StartTime");
            m.Map(x => x.Max, "EndTime");
        });

        this.References(window => window.BookingOrderItem).PropertyRef("BookingWindow").Column("Id").LazyLoad().Nullable().ReadOnly();
        this.Map(x => x.Price);
        this.References(x => x.CustomerRoom).ForeignKey("RoomId").Column("RoomId");
    }

现在,当我执行以下查询时,我返回了没有订单项目的正确预订窗口

Session.QueryOver<BookingWindow>().Where(w => w.CustomerRoom.Id == Guid.Parse(roomId)).Left.JoinQueryOver(bw => bw.BookingOrderItem).WhereRestrictionOn(item => item.Id).IsNull.List<BookingWindow>();
因此,第一个查询被发送到数据库,就像选择订单项列一样,这有点烦人,但真正的问题很快就会出现

选择此Id作为Id2\u 1\u,此价格作为价格2\u 1\u,此室友作为室友2\u 1\u,此开始时间作为开始时间2\u 1\u,此结束时间作为结束时间2\u 1\u,预订Win1\u.Id作为Id4\u 0\u,预订Win1\u.Price作为价格4\u 0\u,预订Win1\u。描述如描述4\u 4\u 0,预订Win1\u.OrderId作为OrderId4\u 0,预订Win1\u.OrderId作为OrderId,预订Win1\u,bookingwin1.OrderItemForeigned作为OrderIte7.U 4.U 0来自Customer.BookingWindows此uu离开外部联接付款。OrderItem bookingwin1_u在此u上。Id=bookingwin1.OrderItemForeigned和bookingwin1.OrderItemTypeId='1',其中此u.RoomId=?并且bookingwin1.Id为空

但是,对于返回的每个预订窗口,都有一个链接订单项目的额外选择,即使我没有要求或需要它。这是在queryover方法中发生的,因此我不会手动迭代返回的预订窗口

选择bookingwin0\u0.Id作为Id4\u 0\u0,bookingwin0\u0.Price作为Price4\u 0\u0,bookingwin0.Description作为Description4\u 0,bookingwin0\u0.OrderId作为OrderId4\u 0,bookingwin0\u0.OrderItemParentId作为OrderIte6\u 4\u 0\u0,bookingwin0\u0.OrderItemForeigned作为OrderItem7\u 4\u 0\u0从付款。OrderItemBookingWin0\u0其中Booking0\u0=?和bookingwin0\u0.OrderItemTypeId='1'


谁能解释一下我在这里犯的错误吗。也许这是显而易见的,但我已经挣扎了几个小时,终于忍无可忍了:

我在映射中看到了一个奇怪的部分:使用引用作为一对一的映射样式。也许这是有意的,但这正是你所面临的问题的根源

首先,正如文件所述[参考文献/多对一][1]

引用用于在两个对象之间创建多对一关系 实体,并应用于多个方面。你指的是一个 单个其他实体,因此使用References方法。有许多/ 一对多是引用关系的另一面,并且 在一侧应用

换句话说,在BookingWindowOrderItemMap的表中存储对BookingWindow的引用。这可能意味着DB设计可能会有更多的OrderItem记录,引用相同的BookingWindow。但也许这就是你想要的,你可以在其他地方检查独特性。我越想理解你的问题,我就越赞成在BookingWindow的列中移动对OrderItem的引用

问题显示:

你的问题。当NHibernate收到BookingWindow列表时,下一步是构建代理。在此过程中,将设置所有valueType/字符串属性,对于引用。。。对于引用,NHibernate尝试准备延迟加载

简化的版本是,在每个属性BookingWindowOrderItem BookingOrderItem中插入一个BookingWindowOrderItem实例的承诺,当第一次接触时返回。在标准情况下,当使用映射引用时,NHibernate此时已从BookingWindow的表中加载引用ID

在您的情况下,此引用ID由虚拟的只读“当前项ID”表示。确实存在的ID。。。但是参考文献没有!我们只选择了BookingWindows,它具有NULL而不是引用

但我们没有由当前实例ID表示的空引用ID

我们用了。左。JoinQueryOver。所以NHibernate确信,它已经在第一个查询中加载了所有数据。。。但是很困惑,因为在他的会话中没有id等于BookingWindow.id/ReferenceId的OrderItem

这就是它试图修复它的原因。。。并再次加载它


这就是为什么NHibernate会做奇怪的选择。没有关于如何解决的建议;这可能是另一个问题和答案…

谢谢你回复这个问题。我现在可以从你的解释中理解这是如何造成的,并决定做出调整来阻止这一切的发生。很高兴看到这一切。祝NHibernate一切顺利。。。学习起来有点挑战性,但一旦你知道如何管理它,那就太棒了。
    public BookingWindowOrderItemMap()
    {
        this.DiscriminatorValue(1);
        this.References(x => x.BookingWindow).LazyLoad().Column("OrderItemForeignId").ForeignKey("OrderItemForeignId");
    }
Session.QueryOver<BookingWindow>().Where(w => w.CustomerRoom.Id == Guid.Parse(roomId)).Left.JoinQueryOver(bw => bw.BookingOrderItem).WhereRestrictionOn(item => item.Id).IsNull.List<BookingWindow>();