我有一个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
的{{1}}属性被映射为_games
。
Bag
以下代码的工作正常,但是,NHibernate在游戏表上执行非常天真的谓词,并在内存中执行排序和过滤。
Bag<Game>("_games", m =>
{
m.Key(k => k.Column("PinballMachineID"));
m.Access(Accessor.Field);
m.Cascade(Cascade.All);
}, r => r.OneToMany());
这非常不理想,因为当我需要的是10时,数据库正在传输数百万条记录。
理想情况下,我希望NHibernate生成如下所示的查询:
-- SLOW! 1,000,000 records
SELECT ...
FROM Games
WHERE PinballMachineID = 123
是否可以配置我的映射,以便我可以在水合物体上执行其他查询(在数据库上)。
我知道我可以使用NHibernate会话来执行linq查询,但我希望这个逻辑成为我的实体的一部分。
答案 0 :(得分:2)
不幸的是,NHibernate并不支持这一点。
当您为PinballMachine
创建映射时,您在ID列上定义了一对多关系,该关系会(懒洋洋地或急切地)获取所有匹配的Game
实体。
我建议的一件事是GetTop10Games
看起来应该属于一个存储库类,而不是该实体的成员。这是使用存储库模式背后的原因之一 - 它封装了所有数据访问逻辑,反过来甚至允许您在真正需要时编写特定的高性能查询,每隔一段时间。这是(不幸或不幸)大多数ORM框架的问题;你永远不知道什么时候某个LINQ提供程序表现不佳,甚至根本无法转换为SQL,所以你想保持你的选项开放。
我肯定会让此方法成为IGameRepository
或IPinballMachineRepository
的成员,并实现类似:
public IList<Games> GetTopGamesForMachine(PinballMachine machine, int maxItems)
{
return Session
.Query<Games>()
.Where(g => g.PinballMachine == machine)
.OrderByDescending(g => g.Score)
.Take(maxItems)
.ToList();
}
答案 1 :(得分:1)
看来,从NHibernate 5开始,已接受的答案是“不幸的是,NHibernate不支持该答案。”不再正确:
从NHibernate 5.0开始,还可以使用System.Linq命名空间中的标准Linq扩展名AsQueryable从实体集合创建查询。
IList<Cat> whiteKittens = cat.Kittens.AsQueryable() .Where(k => k.Color == "white") .ToList();
https://nhibernate.info/doc/nhibernate-reference/querylinq.html
尽管我一直在尝试真的使其切实可行,但是对于NHibernate ORM的“持久性无知”的实用性,我无法确切地说出2019年与2014年之间的其余答案和评论。尽可能...我们会看到的。