在NHibernate实体内部执行查询

本文关键字:执行 查询 内部 实体 NHibernate | 更新日期: 2023-09-27 18:19:27

我有一个PinballMachines的存储库,它返回一个水合的PinballMachine实体。它有一个私人财产,是在那台机器上玩的游戏列表。

弹球机可能会记录数百万场比赛。从PinballMachine开始,我想获得高分,这是排名前十的游戏玩家。

public class PinballMachine
{
    private IList<Game> _games = new List<Game>();  
    public virtual int ID { get; protected set; }
    public virtual IEnumerable<Game> GetTop10Games()
    {
        return _games
            .AsQueryable()
            .OrderByDescending(g => g.Score)
            .Take(10)
            .ToList();
    }
}
public class Game
{
    public virtual Guid ID { get; protected set; }
    public virtual string Name { get; set; }
    public virtual int Score { get; set; }
    public virtual decimal AmountPaid { get; set; }
}

PinballMachine_games属性被映射为Bag

Bag<Game>("_games", m =>
{
    m.Key(k => k.Column("PinballMachineID"));
    m.Access(Accessor.Field);
    m.Cascade(Cascade.All);
}, r => r.OneToMany()); 

下面的代码运行正常,但是,NHibernate在游戏表上执行了一个非常天真的谓词,并在内存中执行排序和筛选。

-- SLOW! 1,000,000 records
SELECT ...
FROM Games
WHERE PinballMachineID = 123

这是非常次优的,因为数据库正在传输数百万条记录,而我只需要10条。

理想情况下,我希望NHibernate生成一个查询,如下所示:

-- FAST! 10 records
SELECT TOP 10 ...
FROM Games
WHERE PinballMachineID = 123
ORDER BY Score DESC

是否可以配置我的映射,以便我可以对水合对象执行额外的查询(在数据库上)。

我知道我可以使用NHibernate会话来执行linq查询,但我希望此逻辑成为我的实体的一部分

在NHibernate实体内部执行查询

不幸的是,NHibernate不支持这一点。

当您为PinballMachine创建映射时,您在ID列上定义了一对多关系,该关系(惰性地或急切地)获取所有匹配的Game实体。

我建议GetTop10Games看起来应该属于存储库类,而不是实体的成员。这就是使用存储库模式的原因之一——它封装了所有的数据访问逻辑,反过来甚至允许您在真正需要时每隔一段时间编写特定的性能查询。这是(不幸的是,或者不是)大多数ORM框架的问题;您永远不知道某个LINQ提供程序何时会表现不佳,甚至根本无法转换为SQL,所以您希望保留您的选项。

我当然会让这个方法成为IGameRepositoryIPinballMachineRepository的成员,并实现它,比如:

public IList<Games> GetTopGamesForMachine(PinballMachine machine, int maxItems)
{
     return Session
         .Query<Games>()
         .Where(g => g.PinballMachine == machine)
         .OrderByDescending(g => g.Score)
         .Take(maxItems)
         .ToList();
}

从NHibernate 5开始,公认的答案"不幸的是,NHibernat不支持"似乎不再正确:

从NHibernate 5.0开始,查询也可以从实体集合中创建,标准的Linq扩展AsQueryable可从System.Linq命名空间中获得。

IList<Cat> whiteKittens =
    cat.Kittens.AsQueryable()
        .Where(k => k.Color == "white")
        .ToList();

https://nhibernate.info/doc/nhibernate-reference/querylinq.html

2019年与2014年相比,关于NHibernate ORM的Persistence Ignorance的实用性,我无法明确地谈论其余的答案和评论,尽管我正在努力让它尽可能地发挥作用。。。我们拭目以待。