来自base的NHibernate QueryOver查询子类

时间:2014-02-01 12:15:32

标签: c# sql nhibernate queryover

我有以下对象:

public abstract class Event {
    public virtual Guid Id { get; set; }
    public virtual string Description { get; set; }
}

public class TeamEvent : Event {
    public virtual Guid Id { get; set; }
    public virtual Team Team { get; set; }
}

public class PersonEvent : Event {
    public virtual Guid Id { get; set; }
    public virtual Person  Person { get; set; }
}

public class Team {
    public virtual Guid Id { get; set; }
    public virtual string Name { get; set; }
}

public class Person {
    public virtual Guid Id { get; set; }
    public virtual string Forename { get; set; }
    public virtual string Surname { get; set; }
}

和映射:                                     

    <discriminator column="Discriminator"
            not-null="true"
            type="System.String"/>

    <subclass
        name="TeamEvent"
        discriminator-value="TeamEvent">
        <many-to-one name="Team" column="TeamId"/>
    </subclass>

    <subclass
        name="PersonalEvent"
        discriminator-value="PersonalEvent">
        <many-to-one name="Person" column="PersonId"/>
    </subclass>
</class>
<class name="Person" table="Persons">
    <id name="Id">
        <generator class="assigned"/>
    </id>
    <property name="Forename"/>
    <property name="Surname"/>
</class>
<class name="Team" table="Teams">
    <id name="Id">
        <generator class="assigned"/>
    </id>
    <property name="TeamName"/>
</class>

我想做的是一个查询,它可以在一个查询中获取所有事件而不管基类型如何填充DTO,如:

public class EventDTO {
   public virtual Guid EventId { get; set; }
   public virtual string EventDescription { get; set; }
   public virtual Guid PersonId { get; set; }
   public virtual string PersonForename { get; set; }
   public virtual string PersonSurname { get; set; }
   public virtual Guid TeamId { get; set; }
   public virtual string TeamName { get; set; }
}

如果是人事件,团队字段为空,反之亦然。

在SQL中我会做类似的事情:

Select * from Events
left join Team on Events.TeamId = Team.Id
left join Person on Events.Person = Person.Id

我无法在QueryOver中执行此操作,因为Events c#对象没有Team / Person的属性。我可以这样做:

var events = session.QueryOver<Event>
               .Where ( /* insert my restrictions */ )
               .Future<Event>();

var teamEvents = session.QueryOver<TeamEvent>
               .Where ( /* insert my restrictions */ )
               .Future<TeamEvent>();
var personEvents = session.QueryOver<PersonEvent>
               .Where ( /* insert my restrictions */ )
               .Future<PersonEvent>();

/* union the results in memory */

从这些查询返回的数据量应该是可以将结果合并到内存中。我的问题是真的有更优雅的方式吗?没有做三个单独的查询。

我应该指出这只是我输入的一个快速示例,以突出我的问题,因此在上面的代码示例中可能会有一些拼写错误等。在现实世界中,系统的其他部分取决于映射,因此不能选择更改模式/映射/对象。

虽然我更喜欢使用查询,但我不介意使用条件或hql,如果可以在那里以更简单的方式进行。此查询也只返回最多20行的实体,这些实体经常被访问并因此存储在二级缓存中,因此即使N + 1也不是主要问题,如果它使代码变得简单。

我还应该指出三个查询不是那么简单的原因是因为每个查询需要复制10个限制,并且还有其他几个事件子类。

感谢您的帮助。

2 个答案:

答案 0 :(得分:2)

当我遇到这样的问题时,我会停止对抗NH并创建数据库视图。我映射为mutable=false。这样我就不会在分页或使用where子句和在客户端合并多个集合时搞得一团糟。

不确定是否有一种更好的方式(特别是当你有相关的表格想要展平时),而不是你想出的那样。正如您所说的那样,您没有太多数据,那么在客户端上合并可能是最好的。

答案 1 :(得分:1)

您应该能够使其与HQL一起使用并编写如下条件:

from Event where (..person event restrictions..) OR (..team event restrictions..)

因为查询抽象基类会强制nHibernate执行左连接,即使你指定了派生属性,它们也是可访问的。所以以简化形式,在查询之后

var personEvents = session
                    .CreateQuery("from Event where Person!=null")
                    .List<Event>();

将为您提供PersonEvent个实体。

所以你应该能够构造更简单的代码但是你真的觉得很奇怪,你想在一个查询中加载不同的子类并立即处理它们等等。未来的查询似乎很有因为它们专注于小(单)块责任 - 如果需要另一项活动,你会怎么做?添加另一个查询而不是一次又一次地修改一个主查询会感觉更好。