在我使用NHibernate with ASP.NET-MVC
进行处理的网络应用中,我们遇到了一些主要的页面加载延迟,所以我被赋予了查看它并进行重构的任务。
当我查看代码时,有很多这样的调用:
public static IEnumerable<Client> GetAllClients()
{
return new EntityManager<Client>()
.GetAll<Client>()
.Where(x => x.IsDeleted == false || x.IsDeleted == null);
}
经过一些调试和搜索后,我发现这实际上对数据库执行了两次单独的调用,一次调用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
并再次返回数据库?这是真的吗?
有更好/更优雅的方式吗?
答案 0 :(得分:2)
当您 over abstract 数据访问时,这是一个非常常见的问题。
List
方法中的GetAllClients
方法调用将导致所有客户端记录从数据库加载到内存中。
我建议每个请求使用一个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();
这使您只能检索所需的字段,而不是从数据库中检索所有字段。
以下是一些有用的帖子:
答案 1 :(得分:1)
这取决于GetAll
EntityManager
的结果类型。如果是IQueryable<Clinet>
,那么在点击数据库之前将应用where,如果它是IEnumerable<Client>
,列表或数组,那么Where
将在内存中执行。
由于GetAllClients
返回IEnumerable<Client>
,后续对Where
的调用将在内存中执行。
此外,在第二个示例中,您可能希望使用Query
(Linq)而不是QueryOver
。 Linq和QueryOver是不同的查询引擎。
答案 2 :(得分:0)
是的,你绝对做出了正确的解决方案。我不太了解NHibernate,但这个案例也与Entity Framework类似。使用会话将避免命中服务器并首先准备查询。一旦准备好查询,它就会命中服务器。
你也可以在这里找到NHibernate中相同where子句的例子
你也可以在这里找到与Nhibernate中的IQueryable概念相关的好教程。
http://ayende.com/blog/2227/implementing-linq-for-nhibernate-a-how-to-guide-part-1