使用 NHibernate 对延迟加载的集合进行分页

发布于 2024-08-24 11:42:45 字数 4812 浏览 4 评论 0原文

我阅读了这篇文章其中 Ayende 指出 NHibernate 可以(与 EF 4 相比):

  • 带有lazy=”extra”的集合 – Lazy extra 意味着 NHibernate 适应 您可能运行的操作 您收藏的顶部。这意味着 blog.Posts.Count 不会强制 整个集合的负载,但是 而是会创建一个“选择计数(*) 来自 BlogId = 1 的帖子” 声明,并且 blog.Posts.Contains() 同样会 结果是单个查询而不是 付出装载整个的代价 收集到内存。
  • 集合过滤器和分页集合 - 这使您可以 定义附加过滤器(包括 分页!)在您的实体之上 集合,这意味着您可以 轻松浏览博客。帖子 集合,而不必加载 整个事情都进入记忆。

所以我决定整理一个测试用例。我创建了陈词滥调的博客模型作为一个简单的演示,其中有两个类,如下所示:

public class Blog
{
    public virtual int Id { get; private set;  }
    public virtual string Name { get; set; }

    public virtual ICollection<Post> Posts { get; private set;  }

    public virtual void AddPost(Post item)
    {
        if (Posts == null) Posts = new List<Post>();
        if (!Posts.Contains(item)) Posts.Add(item);
    }
}

public class Post
{
    public virtual int Id { get; private set; }
    public virtual string Title { get; set; }
    public virtual string Body { get; set; }
    public virtual Blog Blog { get; private set; }
}

我的映射文件如下所示:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
  <class xmlns="urn:nhibernate-mapping-2.2" name="Model.Blog, TestEntityFramework, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="Blogs">
    <id name="Id" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Id" />
      <generator class="identity" />
    </id>
    <property name="Name" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Name" />
    </property>
    <property name="Type" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Type" />
    </property>
    <bag lazy="extra" name="Posts">
      <key>
        <column name="Blog_Id" />
      </key>
      <one-to-many class="Model.Post, TestEntityFramework, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
    </bag>
  </class>
</hibernate-mapping>

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
  <class xmlns="urn:nhibernate-mapping-2.2" name="Model.Post, TestEntityFramework, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="Posts">
    <id name="Id" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Id" />
      <generator class="identity" />
    </id>
    <property name="Title" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Title" />
    </property>
    <property name="Body" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Body" />
    </property>
    <many-to-one class="Model.Blog, TestEntityFramework, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="Blog">
      <column name="Blog_id" />
    </many-to-one>
  </class>
</hibernate-mapping>

我的测试用例如下所示:

        using (ISession session = Configuration.Current.CreateSession()) // this class returns a custom ISession that represents either EF4 or NHibernate
        {
            blogs = (from b in session.Linq<Blog>()
                         where b.Name.Contains("Test")
                         orderby b.Id
                         select b);

            Console.WriteLine("# of Blogs containing 'Test': {0}", blogs.Count());
            Console.WriteLine("Viewing the first 5 matching Blogs.");

            foreach (Blog b in blogs.Skip(0).Take(5))
            {
                Console.WriteLine("Blog #{0} \"{1}\" has {2} Posts.", b.Id, b.Name, b.Posts.Count);
                Console.WriteLine("Viewing first 5 matching Posts.");

                foreach (Post p in b.Posts.Skip(0).Take(5))
                {
                    Console.WriteLine("Post #{0} \"{1}\" \"{2}\"", p.Id, p.Title, p.Body);
                }
            }
        }

使用 lazy="extra",调用 < code>b.Posts.Count 确实执行了 SELECT COUNT(Id)... 这很棒。但是,b.Posts.Skip(0).Take(5) 只是抓取 Blog.Id = ?id 的所有帖子,然后应用程序端的 LINQ 只是从结果中取出前 5 个帖子收藏。

什么给?

I read this article where Ayende states NHibernate can (compared to EF 4):

  • Collection with lazy=”extra” – Lazy extra means that NHibernate adapts to
    the operations that you might run on
    top of your collections. That means
    that blog.Posts.Count will not force a
    load of the entire collection, but
    rather would create a “select count(*)
    from Posts where BlogId = 1”
    statement, and that
    blog.Posts.Contains() will likewise
    result in a single query rather than
    paying the price of loading the entire
    collection to memory.
  • Collection filters and paged collections - this allows you to
    define additional filters (including
    paging!) on top of your entities
    collections, which means that you can
    easily page through the blog.Posts
    collection, and not have to load the
    entire thing into memory.

So I decided to put together a test case. I created the cliché Blog model as a simple demonstration, with two classes as follows:

public class Blog
{
    public virtual int Id { get; private set;  }
    public virtual string Name { get; set; }

    public virtual ICollection<Post> Posts { get; private set;  }

    public virtual void AddPost(Post item)
    {
        if (Posts == null) Posts = new List<Post>();
        if (!Posts.Contains(item)) Posts.Add(item);
    }
}

public class Post
{
    public virtual int Id { get; private set; }
    public virtual string Title { get; set; }
    public virtual string Body { get; set; }
    public virtual Blog Blog { get; private set; }
}

My mappings files look like this:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
  <class xmlns="urn:nhibernate-mapping-2.2" name="Model.Blog, TestEntityFramework, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="Blogs">
    <id name="Id" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Id" />
      <generator class="identity" />
    </id>
    <property name="Name" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Name" />
    </property>
    <property name="Type" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Type" />
    </property>
    <bag lazy="extra" name="Posts">
      <key>
        <column name="Blog_Id" />
      </key>
      <one-to-many class="Model.Post, TestEntityFramework, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
    </bag>
  </class>
</hibernate-mapping>

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
  <class xmlns="urn:nhibernate-mapping-2.2" name="Model.Post, TestEntityFramework, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="Posts">
    <id name="Id" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Id" />
      <generator class="identity" />
    </id>
    <property name="Title" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Title" />
    </property>
    <property name="Body" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Body" />
    </property>
    <many-to-one class="Model.Blog, TestEntityFramework, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="Blog">
      <column name="Blog_id" />
    </many-to-one>
  </class>
</hibernate-mapping>

My test case looks something like this:

        using (ISession session = Configuration.Current.CreateSession()) // this class returns a custom ISession that represents either EF4 or NHibernate
        {
            blogs = (from b in session.Linq<Blog>()
                         where b.Name.Contains("Test")
                         orderby b.Id
                         select b);

            Console.WriteLine("# of Blogs containing 'Test': {0}", blogs.Count());
            Console.WriteLine("Viewing the first 5 matching Blogs.");

            foreach (Blog b in blogs.Skip(0).Take(5))
            {
                Console.WriteLine("Blog #{0} \"{1}\" has {2} Posts.", b.Id, b.Name, b.Posts.Count);
                Console.WriteLine("Viewing first 5 matching Posts.");

                foreach (Post p in b.Posts.Skip(0).Take(5))
                {
                    Console.WriteLine("Post #{0} \"{1}\" \"{2}\"", p.Id, p.Title, p.Body);
                }
            }
        }

Using lazy="extra", the call to b.Posts.Count does do a SELECT COUNT(Id)... which is great. However, b.Posts.Skip(0).Take(5) just grabs all Posts for Blog.Id = ?id, and then LINQ on the application side is just taking the first 5 from the resulting collection.

What gives?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

情仇皆在手 2024-08-31 11:42:45

我很确定(阅读评论)他正在谈论 ISession 的 CreateFilter。

您可以像这样进行分页(来自文档 13.13):

Collections are pageable by using the IQuery interface with a filter:

IQuery q = s.CreateFilter( collection, "" ); // the trivial filter
q.setMaxResults(PageSize); 
q.setFirstResult(PageSize * pageNumber); 
IList page = q.List();

或者(来自文档 17.1.4):

s.CreateFilter( lazyCollection, "").SetFirstResult(0).SetMaxResults(10).List();

这并不像使用 System.Linq 方法那么顺利。我想他们有时也会加入语法。

I'm pretty sure (reading the comments) that he's talking about the CreateFilter of ISession.

You can do paging like this (from the docs 13.13):

Collections are pageable by using the IQuery interface with a filter:

IQuery q = s.CreateFilter( collection, "" ); // the trivial filter
q.setMaxResults(PageSize); 
q.setFirstResult(PageSize * pageNumber); 
IList page = q.List();

Or (from the docs 17.1.4):

s.CreateFilter( lazyCollection, "").SetFirstResult(0).SetMaxResults(10).List();

That's is not as smooth as using the System.Linq methods. I guess they'll join the syntax some time too.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文