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

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

要看EntityManagerGetAll的结果类型是什么。如果它是 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();

这允许您只检索需要的字段而不是数据库中的所有字段。

这里有一些有用的帖子: