一种无需多个数据库调用即可查询的方法
A way to query without multiple DB calls
我正在使用 NHibernate with ASP.NET-MVC
开发一个网络应用程序,我们在页面加载时遇到了一些严重的滞后,所以我被赋予了调查它并进行一些重构的任务。
我翻了一下代码,有很多这样的调用:
public static IEnumerable<Client> GetAllClients()
{
return new EntityManager<Client>()
.GetAll<Client>()
.Where(x => x.IsDeleted == false || x.IsDeleted == null);
}
经过一些调试和搜索后,我发现这实际上对数据库执行了 2 次单独的调用,一次调用 GetAll
然后它应用 Where
并进行第二次调用,或类似的调用.我在 SO 上发现了另一个问题,这让我决定改用会话:
public static IEnumerable<Client> GetAllClients()
{
using (ISession session = NHibernateSessionFactoryManager.Factory.OpenSession())
{
return session.QueryOver<Client>()
.Where(x => x.IsDeleted == false || x.IsDeleted == null)
.List();
}
}
这要好得多,因为它在进入数据库之前考虑了 Where
子句。但是,我仍然看到调用 GetAll()
但随后在其上应用另一个 Where
的方法:
var Clients = GetAllClients().Where(n => n.Location == "someCity");
我的猜测是这首先使用会话调用 GetAll()
,然后应用新的 Where
并再次返回数据库?这是真的吗?
有没有better/more优雅的方法来做到这一点?
是的,您的解决方案绝对正确。我不太了解 NHibernate,但这种情况也类似于 Entity Framework。使用会话将避免命中服务器并首先准备查询。准备好查询后,它将访问服务器。
您还可以在此处找到 NHibernate 中相同 where 子句的示例
Where clause in NHibernate
您还可以在此处找到与 Nhibernate 中的 IQueryable 概念相关的很好的教程。
http://ayende.com/blog/2227/implementing-linq-for-nhibernate-a-how-to-guide-part-1
要看EntityManager
的GetAll
的结果类型是什么。如果它是 IQueryable<Clinet>
那么 where 将在访问数据库之前应用,如果它是 IEnumerable<Client>
,或者列表,或者数组那么 Where
将在内存中执行。
因为 GetAllClients
returns IEnumerable<Client>
对 Where
的后续调用将在内存中执行。
此外,在第二个示例中,您可能希望使用 Query
(Linq) 而不是 QueryOver
。 Linq 和 QueryOver 是不同的查询引擎。
当您过度抽象数据访问时,这是一个非常常见的问题。
在GetAllClients
方法中调用List
方法将导致所有客户端记录从数据库加载到内存中。
我建议每个请求使用一个 NHibernate ISession
,而不是每个方法调用一个 ISession
。并让您的方法返回 IQueryable
而不是 IEnumerable
。像这样的东西(NHibernate 3.0+):
using NHibernate;
using NHibernate.Linq;
public class EntityManager<T> {
private ISession _session;
public EntityManager(ISession session) {
_session = session;
}
public IQueryable<T> GetAllClients() {
return _session.Query<T>();
}
}
然后您可以按国家筛选:
GetAllClients().Where(it => it.Location == "city").ToList();
或者做投影:
var clientShortInfos = GetAllClients().Where(it => it.Location == "city")
.Select(it => new
{
Id = it.Id,
FullName = it.FullName
})
.ToList();
这允许您只检索需要的字段而不是数据库中的所有字段。
这里有一些有用的帖子:
我正在使用 NHibernate with ASP.NET-MVC
开发一个网络应用程序,我们在页面加载时遇到了一些严重的滞后,所以我被赋予了调查它并进行一些重构的任务。
我翻了一下代码,有很多这样的调用:
public static IEnumerable<Client> GetAllClients()
{
return new EntityManager<Client>()
.GetAll<Client>()
.Where(x => x.IsDeleted == false || x.IsDeleted == null);
}
经过一些调试和搜索后,我发现这实际上对数据库执行了 2 次单独的调用,一次调用 GetAll
然后它应用 Where
并进行第二次调用,或类似的调用.我在 SO 上发现了另一个问题,这让我决定改用会话:
public static IEnumerable<Client> GetAllClients()
{
using (ISession session = NHibernateSessionFactoryManager.Factory.OpenSession())
{
return session.QueryOver<Client>()
.Where(x => x.IsDeleted == false || x.IsDeleted == null)
.List();
}
}
这要好得多,因为它在进入数据库之前考虑了 Where
子句。但是,我仍然看到调用 GetAll()
但随后在其上应用另一个 Where
的方法:
var Clients = GetAllClients().Where(n => n.Location == "someCity");
我的猜测是这首先使用会话调用 GetAll()
,然后应用新的 Where
并再次返回数据库?这是真的吗?
有没有better/more优雅的方法来做到这一点?
是的,您的解决方案绝对正确。我不太了解 NHibernate,但这种情况也类似于 Entity Framework。使用会话将避免命中服务器并首先准备查询。准备好查询后,它将访问服务器。
您还可以在此处找到 NHibernate 中相同 where 子句的示例
Where clause in NHibernate
您还可以在此处找到与 Nhibernate 中的 IQueryable 概念相关的很好的教程。
http://ayende.com/blog/2227/implementing-linq-for-nhibernate-a-how-to-guide-part-1
要看EntityManager
的GetAll
的结果类型是什么。如果它是 IQueryable<Clinet>
那么 where 将在访问数据库之前应用,如果它是 IEnumerable<Client>
,或者列表,或者数组那么 Where
将在内存中执行。
因为 GetAllClients
returns IEnumerable<Client>
对 Where
的后续调用将在内存中执行。
此外,在第二个示例中,您可能希望使用 Query
(Linq) 而不是 QueryOver
。 Linq 和 QueryOver 是不同的查询引擎。
当您过度抽象数据访问时,这是一个非常常见的问题。
在GetAllClients
方法中调用List
方法将导致所有客户端记录从数据库加载到内存中。
我建议每个请求使用一个 NHibernate ISession
,而不是每个方法调用一个 ISession
。并让您的方法返回 IQueryable
而不是 IEnumerable
。像这样的东西(NHibernate 3.0+):
using NHibernate;
using NHibernate.Linq;
public class EntityManager<T> {
private ISession _session;
public EntityManager(ISession session) {
_session = session;
}
public IQueryable<T> GetAllClients() {
return _session.Query<T>();
}
}
然后您可以按国家筛选:
GetAllClients().Where(it => it.Location == "city").ToList();
或者做投影:
var clientShortInfos = GetAllClients().Where(it => it.Location == "city")
.Select(it => new
{
Id = it.Id,
FullName = it.FullName
})
.ToList();
这允许您只检索需要的字段而不是数据库中的所有字段。
这里有一些有用的帖子: