分页在一个延迟加载集合与NHibernate [英] Paging over a lazy-loaded collection with NHibernate

查看:132
本文介绍了分页在一个延迟加载集合与NHibernate的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我阅读了本文其中Ayende表示NHibernate可以(与EF 4相比):



  • 集合与lazy =额外 - 延迟额外意味着NHibernate适应
    您可能运行在
    顶部的集合的操作。这意味着
    ,blog.Posts.Count不会强制整个集合的
    加载,但是
    相反会创建一个select count(*)
    from Posts where BlogId = 1
    语句,并且
    blog.Posts.Contains()将同样
    导致单个查询,而不是
    支付加载整个
    集合的价格

  • 集合过滤器和分页集合 - 这允许您
    在实体顶部定义其他过滤器(包括
    paging!)
    集合,这意味着你可以
    轻松浏览blog.Posts
    集合,而不必将
    整个内容加载到内存中。


所以我决定把一个测试用例放在一起。我创建了cliché博客模型作为一个简单的示范,有两个类如下:

  public class Blog 
{
public virtual int Id {get;私人集}
public virtual string Name {get;组; }

public virtual ICollection< Post>帖子{get;私人集}

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;私人集}
public virtual string Title {get;组; }
public virtual string Body {get;组; }
public virtual Blog Blog {get;私人集}
}

我的映射文件看起来像这样:

 < hibernate-mapping xmlns =urn:nhibernate-mapping-2.2default-access =propertyauto-import =true nonedefault-lazy =true> 
< class xmlns =urn:nhibernate-mapping-2.2name =Model.Blog,TestEntityFramework,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = nulltable =Blogs>
< id name =Idtype =System.Int32,mscorlib,Version = 4.0.0.0,Culture = neutral,PublicKeyToken = b77a5c561934e089>
< column name =Id/>
< generator class =identity/>
< / id>
< property name =Nametype =System.String,mscorlib,Version = 4.0.0.0,Culture = neutral,PublicKeyToken = b77a5c561934e089>
< column name =Name/>
< / property>
< property name =Typetype =System.Int32,mscorlib,Version = 4.0.0.0,Culture = neutral,PublicKeyToken = b77a5c561934e089>
< column name =Type/>
< / property>
< bag lazy =extraname =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.2default-access =propertyauto-import =truedefault-cascade =nonedefault-lazy = true>
< class xmlns =urn:nhibernate-mapping-2.2name =Model.Post,TestEntityFramework,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = nulltable =Posts>
< id name =Idtype =System.Int32,mscorlib,Version = 4.0.0.0,Culture = neutral,PublicKeyToken = b77a5c561934e089>
< column name =Id/>
< generator class =identity/>
< / id>
< property name =Titletype =System.String,mscorlib,Version = 4.0.0.0,Culture = neutral,PublicKeyToken = b77a5c561934e089>
< column name =Title/>
< / property>
< property name =Bodytype =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 = nullname =Blog>
< column name =Blog_id/>
< / many-to-one>
< / class>
< / hibernate-mapping>

我的测试用例看起来像这样:

  using(ISession session = Configuration.Current.CreateSession())//这个类返回一个自定义的ISession,代表EF4或NHibernate 
{
blogs = from b in session.Linq< Blog>()
其中b.Name.Contains(Test)
orderby b.Id
select b);

Console.WriteLine(#of Blogs containing'Test':{0},blogs.Count());
Console.WriteLine(查看前5个匹配的博客。

foreach(博客b在blogs.Skip(0).Take(5))
{
Console.WriteLine(Blog#{0} \{1} \有{2}帖子。,b.Id,b.Name,b.Posts.Count);
Console.WriteLine(查看前5个匹配的帖子。

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

使用 lazy = extra,调用 b.Posts.Count 会执行 SELECT COUNT(Id)... 这是伟大的。但是, b.Posts.Skip(0).Take(5)刚刚抓住Blog.Id =?id的所有帖子,然后LINQ在应用程序端

解决方案

>我很肯定(阅读评论),他说的是ISession的CreateFilter。



您可以这样(从文档13.13)进行分页:

 通过使用带有过滤器的IQuery接口可以分页集合:

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 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?

解决方案

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.

这篇关于分页在一个延迟加载集合与NHibernate的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆