2009-09-11 42 views
3

我有一个数据库模式,存储一个“页面”与许多“修订”。就像一个简单的维基。NHibernate:有时我想要一个项目,其他时间我想要他们全部

90%的时间,当我加载一个页面时,我只是对最新版本感兴趣。但是,有时候我想要所有修订。

使用NHibernate,我可以将页面映射到修订版,并告诉它延迟加载。但是,当我访问最新的修订版时,它将加载所有其他修订版 - 这是对I/O的极大浪费。

我的页面类目前类似于:

public class Page 
{ 
    public Page() 
    { 
     Revisions = new HashedSet<Revision>(); 
    } 

    public virtual ISet<Revision> Revisions { get; private set; } 

    public virtual Revision LatestRevision 
    { 
     get { return Revisions.OrderByDescending(x => x.Revised).FirstOrDefault(); } 
    } 

    public virtual Revision Revise() 
    { 
     var revision = new Revision(); 
     // ... 
     revision.Entry = this; 
     revision.Revised = DateTime.UtcNow; 
     Revisions.Add(revision); 
     return revision; 
    } 
} 

我怎么会对此建模,这样当页面加载LatestRevision自动加载,但其他版本是延迟加载,如果,例如,我试图迭代它们?

我特别喜欢一种与LINQ to NHibernate一起工作的解决方案,但是使用ICriteria(如果必须的话,甚至SQL)也足够好。

回答

0

我结束了在这里与解决方案去:

Partially Populate Child Collection with NHibernate

我的页面现在具有以下属性:

public virtual Revision CurrentRevision 
{ 
    get 
    { 
     return _revision ?? Revisions.OrderByDescending(x => x.Revised).FirstOrDefault(); 
    } 
    set { _revision = value; } 
} 

public virtual ISet<Revision> Revisions { get; private set; } 

加载代码:

public Page GetPage(string name) 
{ 
    var entryHash = (Hashtable)_session.CreateCriteria<Page>("page") 
     .Add(Restrictions.Eq("page.Name", name)) 
     .CreateCriteria("Revisions", "rev") 
      .AddOrder(Order.Desc("rev.Revised")) 
     .SetMaxResults(1) 
     .SetResultTransformer(Transformers.AliasToEntityMap) 
     .UniqueResult(); 
    var page = (Page)entryHash["page"]; 
    page.LatestRevision = (Revision)entryHash["rev"]; 
    return page; 
} 

NHProf将其显示为现在正在执行的唯一查询,这是完美的:

SELECT top 1 this_.Id   as Id3_1_, 
       this_.Name   as Name3_1_, 
       this_.Title   as Title3_1_, 
       rev1_.Id   as Id0_0_, 
       rev1_.Body   as Body0_0_, 
       rev1_.Revised  as Revised0_0_, 
       .... 
FROM  [Page] this_ 
     inner join [Revision] rev1_ 
      on this_.Id = rev1_.PageId 
WHERE this_.Name = 'foo' /* @p0 */ 
ORDER BY rev1_.Revised desc 
0

我正在处理类似的问题。

怎么样映射完全符合你在那里。 LatestRevision属性可以映射为修订表的一对一映射,修订版会像您已经掌握的一样。你将不得不有一个setter(可能是私人的),并在修改方法中管理关系。

一个问题是,LatestRevision仍然会在一组修订中。

我也碰到过一篇由Ayende发表的帖子,它使用公式属性作为属性,我从来没有用过它,但看起来可能适合账单。

0

您可以在映射文件中使用派生属性(而不是在属性中执行逻辑)。它可能看起来像这样:

<property name="LatestRevision" 
      forumla="select top r.f1, r.f2, r.etc from Revisions order by revised desc" 
      type="Revision" /> 

有关此方法的更多信息,请搜索'nhibernate派生属性'。

https://www.hibernate.org/hib_docs/nhibernate/1.2/reference/en/html_single/

+0

我刚试过这个,但看起来我只能使用一个公式,如果它返回一个简单的类型。对于我的复杂修改对象,我需要为它编写一个IUserType - 这会让我失去调用数据读取器的路径,这似乎打败了使用NH的目的:) – 2009-09-11 14:56:15

+0

虽然很高兴知道。我不知道它仅限于简单的类型。 – 2009-09-11 21:38:39

0

添加LatestRevision柱(保持它),并映射到。 它会为你节省很多头痛。

+0

嗯,我喜欢这个解决方案,因为它可以让我只进行内部连接,而不必选择max orderby等。 – 2009-09-11 14:53:16

+0

这样做的一个问题是,页面依赖于修订版本,修订版本取决于页面 - 这意味着我' d至少需要其中一列为空,并使用两个事务使其工作:( – 2009-09-11 14:58:22

0

什么问题有最新版本属性和页表中的相应列?

public class Page 
{ 
    public Page() 
    { 
     Revisions = new HashedSet<Revision>(); 
    } 

    public virtual ISet<Revision> Revisions { get; private set; } // lazy="true" 

    public virtual Revision LatestRevision { get; private set; } // lazy="false" 

    public virtual Revision Revise() 
    { 
     var revision = new Revision(); 
     // ... 
     revision.Entry = this; 
     revision.Revised = DateTime.UtcNow; 
     Revisions.Add(revision); 
     LatestRevision = revision; // <- there you have latest revision 
     return revision; 
    } 
} 
+0

与弗雷迪的答案一样 - 除非有修订,否则无法创建页面,但不能在没有修订的情况下一个页面除非你让最新的视觉可以为空,这对我来说似乎是错误的 – 2009-09-13 23:35:11

+0

对不起,也许是为时已晚,也许你应该在创建页面时创建第一个修订版本? – dariol 2009-09-14 23:14:03

相关问题