1

我有一个项目列表(不会更改),因此使用 NHibernate 查询它并转换为如下所示的内存列表

List<Items> myItems = _repository.All().ToList();

稍后当用户进行搜索并单击搜索按钮时,我会在下面执行

myItems.Where(item => item.Name == searchTerm)

我看到第一个代码导致以下查询(等效不准确)

SELECT * FROM Items

然后第二个代码对数据库造成另一次命中,即使它在内存对象中。

SELECT * FROM Items Where Name = 'Stackoverflow'

我该怎么做才能停止不必要的查询?

每个 item 可以属于多个用户,因此 Item 具有如下映射

HasMany(x => x.Users).KeyColumn("UserId");

在显示拥有项目的用户数量的页面中,我有如下代码

foreach(Item item in AllItems)
{

   var itemName = item.Name;
   var itemUserCount = item.Users.Count;

}

上面导致对用户表的 N 次查询,即如果项目计数 = 100,则在用户表上执行 100 次查询以获取计数。

How do i optimize the above mapping to work efficiently?

资源:

http://www.hibernatingrhinos.com/products/nhprof/learn/alert/SelectNPlusOne

4

2 回答 2

1

我认为的一个问题是用户集合人口,分别为每个项目完成。解决 N+1 问题的有效方法是使用批量加载。可以在文档19.1.5 中找到非常清楚的解释。使用批量获取

防止流利的NHibernate 选择 n+1问题/答案有点旧,因此在您的情况下使用的语法应该是:

HasMany(x => x.Users)
    .KeyColumn("UserId")
    .BatchSize(20) // here we go
    ;

这将显着减少查询量。因为会同时加载很多用户。

于 2013-11-01T03:40:29.580 回答
0

你最好只提取你需要的数据(使用 LINQ 提供程序):

var result = session.Query<Item>().Where(x => x.Name == searchTerm).Select(x => new { x.Name, UserCount = x.Users.Count() });

(如果您需要在拉取数据的函数之外使用该数据,则必须选择专用类,或者至少选择一个Tuple

如果您真的想提取所有数据(使用QueryOverAPI):

var result = session.QueryOver<Item>().Fetch(x => x.User).Eager.List();

这可能会创建一个LEFT JOIN,但您需要急切地获取您可能需要的所有其他实体。

有一种简单的方法可以查看您是否有 N+1 问题:在提取数据后立即处理会话。如果你在任何地方访问一个非急切获取的相关实体,你会得到一个异常,这使得调试这种情况变得更加容易:

IList<Item> list;
using(var session = sessionFactory.OpenSession())
{
    list = session.QueryOver<Item>().Fetch(x => x.User).Eager.List();
}
var otherStuff = list.Select(x => x.OtherItem.Price).ToList(); // Boom, exception! OtherItem was not eagerly fetched.
于 2013-10-31T18:07:44.010 回答