【发布时间】:2011-01-26 17:10:09
【问题描述】:
我读到 this article Ayende 声明 NHibernate 可以(与 EF 4 相比):
- 带有lazy=”extra”的集合——惰性额外意味着NHibernate适应 您可能运行的操作 您的收藏品的顶部。这意味着 那 blog.Posts.Count 不会强制 加载整个集合,但是 宁愿创建一个“选择计数(*) 来自 BlogId = 1 的帖子” 声明,并且 blog.Posts.Contains() 同样会 导致单个查询而不是 支付整个装载的代价 收集到内存。
- 集合过滤器和分页集合 - 这允许您 定义额外的过滤器(包括 分页!)在您的实体之上 集合,这意味着您可以 轻松浏览博客。帖子 集合,而不必加载 整件事都记在心里了。
所以我决定整理一个测试用例。我创建了 cliché Blog 模型作为一个简单的演示,有两个类如下:
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",对b.Posts.Count 的调用确实会执行SELECT COUNT(Id)...,这很棒。但是,b.Posts.Skip(0).Take(5) 只是获取 Blog.Id = ?id 的所有帖子,然后应用程序端的 LINQ 只是从结果集合中获取前 5 个。
什么给了?
【问题讨论】:
-
+1 提出一个好问题。我一直想知道 NHibernates 的延迟加载功能,但一直没有时间尝试。
-
可能关于收集过滤器的 LINQ to NHibernate 部分没有实现
-
使用lazy="extra"只有一个警告。我们开始在我们的应用程序上使用它,一切正常,直到我们部署到 Sql Server 2000 机器(仍然是客户认可的软件)。因为我们使用 guid 作为我们的身份字段,所以我们开始收到异常“无法对唯一标识符执行计数”(大约)。我查看了 NH 源代码,发现是额外的延迟加载选择了第一个键列并执行了“select count(
)”,这是我们的 GUID 字段。
标签: c# nhibernate collections pagination lazy-loading