一种无需多次数据库调用即可查询的方法

时间:2015-04-22 12:53:41

标签: c# asp.net-mvc nhibernate

在我使用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并再次返回数据库?这是真的吗?

有更好/更优雅的方式吗?

3 个答案:

答案 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子句的例子

Where clause in NHibernate

你也可以在这里找到与Nhibernate中的IQueryable概念相关的好教程。

http://ayende.com/blog/2227/implementing-linq-for-nhibernate-a-how-to-guide-part-1