<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:copyright="http://blogs.law.harvard.edu/tech/rss" xmlns:image="http://purl.org/rss/1.0/modules/image/">
    <channel>
        <title>NHibernate</title>
        <link>http://ayende.com/Blog/category/510.aspx</link>
        <description>NHibernate</description>
        <language>en-US</language>
        <copyright>Ayende Rahien</copyright>
        <managingEditor>Ayende@ayende.com</managingEditor>
        <generator>Subtext Version 2.0.0.0</generator>
        <item>
            <title>NHibernate donation campaign</title>
            <link>http://ayende.com/Blog/archive/2010/02/27/nhibernate-donation-campaign.aspx</link>
            <description>&lt;p&gt;NHibernate is the most popular Open Source Object Relational Mapper in the .NET framework. As an Open Source project, all the work done on it is done for free.  We would like to be able to dedicate more time to NHibernate, but even as a labor of love, the amount of time that we can spend on a free project is limited.&lt;/p&gt;  &lt;p&gt;In order to facilitate that, we opened a &lt;a href="http://pledgie.com/campaigns/8615"&gt;donation campaign&lt;/a&gt; that will allow you to donate money to the project.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.pledgie.com/campaigns/8615"&gt;&lt;img border="0" alt="Click here to lend your support to: NHibernate and make a donation at www.pledgie.com !" src="http://www.pledgie.com/campaigns/8615.png?skin_name=chrome" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;What is this money going to be used for?&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;This money will go directly to NHibernate development, primarily to sponsor the time required development of NHibernate itself.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Donation Matching&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Moreover, my company, &lt;a href="http://hibernatingrhinos.com/"&gt;Hibernating Rhinos&lt;/a&gt;, is going to &lt;em&gt;match any donation &lt;/em&gt;to this campaign (to a total limit of 5,000$), as a way to give back to the NHibernate project for the excellent software it produced.&lt;/p&gt;  &lt;p&gt;In addition to that, my company will also sponsor any resources need for the project, such as production servers (the &lt;a href="http://jira.nhforge.org"&gt;NHibernate’s Jira&lt;/a&gt; is already running on our servers), build machines, etc.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Why should you donate?&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;If you are a user of NHibernate, you gained a lot from build on such a solid foundation. We ask to you to donate so that we can make the project even better. If your company uses NHibernate, ask it to donate to this campaign.&lt;/p&gt;  &lt;p&gt;Thanks,&lt;/p&gt;  &lt;p&gt;~Ayende&lt;/p&gt;&lt;img src="http://ayende.com/Blog/aggbug/11335.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Ayende Rahien</dc:creator>
            <guid>http://ayende.com/Blog/archive/2010/02/27/nhibernate-donation-campaign.aspx</guid>
            <pubDate>Sat, 27 Feb 2010 11:21:00 GMT</pubDate>
            <wfw:comment>http://ayende.com/Blog/comments/11335.aspx</wfw:comment>
            <comments>http://ayende.com/Blog/archive/2010/02/27/nhibernate-donation-campaign.aspx#feedback</comments>
            <slash:comments>11</slash:comments>
            <wfw:commentRss>http://ayende.com/Blog/comments/commentRss/11335.aspx</wfw:commentRss>
        </item>
        <item>
            <title>What is the story behind the Entity Framework vs. NHibernate posts?</title>
            <link>http://ayende.com/Blog/archive/2010/01/29/what-is-the-story-behind-the-entity-framework-vs.-nhibernate.aspx</link>
            <description>&lt;p&gt;A while ago I posted several posts about &lt;a href="http://ayende.com/Blog/archive/2010/01/05/nhibernate-vs.-entity-framework-4.0.aspx"&gt;EF vs. NH&lt;/a&gt;. They generated quite a bit of commentary, but while I enjoyed the discussion, I had an ulterior motive for doing so.&lt;/p&gt;  &lt;p&gt;I wanted to do this as a way to do a comparative research about the actual features that people would like to see in NHibernate.&lt;/p&gt;&lt;img src="http://ayende.com/Blog/aggbug/11297.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Ayende Rahien</dc:creator>
            <guid>http://ayende.com/Blog/archive/2010/01/29/what-is-the-story-behind-the-entity-framework-vs.-nhibernate.aspx</guid>
            <pubDate>Fri, 29 Jan 2010 10:00:00 GMT</pubDate>
            <wfw:comment>http://ayende.com/Blog/comments/11297.aspx</wfw:comment>
            <comments>http://ayende.com/Blog/archive/2010/01/29/what-is-the-story-behind-the-entity-framework-vs.-nhibernate.aspx#feedback</comments>
            <slash:comments>42</slash:comments>
            <wfw:commentRss>http://ayende.com/Blog/comments/commentRss/11297.aspx</wfw:commentRss>
        </item>
        <item>
            <title>NHibernate new feature: No proxy associations</title>
            <link>http://ayende.com/Blog/archive/2010/01/28/nhibernate-new-feature-no-proxy-associations.aspx</link>
            <description>&lt;p&gt;About three weeks ago I introduced the problem of &lt;a href="http://ayende.com/Blog/archive/2010/01/09/nhibernate-polymorphic-associations-and-ghost-objects.aspx"&gt;ghost objects in NHibernate&lt;/a&gt;. In short, given the following model:&lt;/p&gt;  &lt;p&gt;&lt;img alt="image" src="http://ayende.com/Blog/images/ayende_com/Blog/WindowsLiveWriter/NHibernatepolymorphicassociationsandghos_AB95/image_thumb.png" /&gt;&lt;/p&gt;  &lt;p&gt;This code will &lt;em&gt;not &lt;/em&gt;produce the expected result:&lt;/p&gt;  &lt;blockquote&gt;   &lt;pre class="csharpcode"&gt;var comment = s.Get&amp;lt;Comment&amp;gt;(8454);
&lt;span class="kwrd"&gt;if&lt;/span&gt;(comment.Post &lt;span class="kwrd"&gt;is&lt;/span&gt; Article)
{
   &lt;span class="rem"&gt;//&lt;/span&gt;
}&lt;/pre&gt;
  &lt;style type="text/css"&gt;&lt;![CDATA[
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;&lt;/blockquote&gt;

&lt;p&gt;You can check the actual post for the details, it related to proxying and when NHibernate decides to load a lazy loaded instance. In short, however, comment.Post is a lazy loaded object, and NHibernate, at this point in time, has no idea what it is. But since it must return &lt;em&gt;something&lt;/em&gt;, it returns a proxy of Post, which will load the actual instance when needed. That leads to some problems when you want to down cast the value.&lt;/p&gt;

&lt;p&gt;Well, I got fed up with explaining about this and set about to fix the issue. NHibernate now contains the following option:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;many-to-one&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;="Post"&lt;/span&gt; &lt;span class="attr"&gt;lazy&lt;/span&gt;&lt;span class="kwrd"&gt;="no-proxy"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;
  &lt;style type="text/css"&gt;&lt;![CDATA[
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;&lt;/blockquote&gt;

&lt;p&gt;When lazy is set to no-proxy, the following things happen:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The association is &lt;em&gt;still&lt;/em&gt; lazy loaded (note that in older versions of NHibernate, setting it to no-proxy would trigger eager loading, this is no longer the case).&lt;/li&gt;

  &lt;li&gt;The first time that you &lt;em&gt;access the property&lt;/em&gt; the value will be loaded from the database, and the actual type will be returned.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In short, this should completely resolve the issue.&lt;/p&gt;

&lt;p&gt;However, not the key phrase here, like lazy properties, this work by intercepting the property load, so if you want to take advantage of this feature you should use the property to access the value.&lt;/p&gt;&lt;img src="http://ayende.com/Blog/aggbug/11296.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Ayende Rahien</dc:creator>
            <guid>http://ayende.com/Blog/archive/2010/01/28/nhibernate-new-feature-no-proxy-associations.aspx</guid>
            <pubDate>Thu, 28 Jan 2010 10:46:00 GMT</pubDate>
            <wfw:comment>http://ayende.com/Blog/comments/11296.aspx</wfw:comment>
            <comments>http://ayende.com/Blog/archive/2010/01/28/nhibernate-new-feature-no-proxy-associations.aspx#feedback</comments>
            <slash:comments>32</slash:comments>
            <wfw:commentRss>http://ayende.com/Blog/comments/commentRss/11296.aspx</wfw:commentRss>
        </item>
        <item>
            <title>NHibernate new feature: Lazy Properties</title>
            <link>http://ayende.com/Blog/archive/2010/01/27/nhibernate-new-feature-lazy-properties.aspx</link>
            <description>&lt;p&gt;This feature is now available on the NHibernate trunk. Please note that it is currently only available when using the Castle Proxy Factory.&lt;/p&gt;  &lt;p&gt;Lazy properties is a very simple feature. Let us go back to my usual blog example, and take a look at the Post entity:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://ayende.com/Blog/images/ayende_com/Blog/WindowsLiveWriter/NHibernatenewfeatureLazyProperties_10DA6/image_2.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://ayende.com/Blog/images/ayende_com/Blog/WindowsLiveWriter/NHibernatenewfeatureLazyProperties_10DA6/image_thumb.png" width="235" height="272" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;As you can see, it is pretty simple example, but we have a problem. The Text property may contain a &lt;em&gt;lot &lt;/em&gt;of text, and we &lt;em&gt;don’t&lt;/em&gt; want to load that unless we explicitly asks for it.&lt;/p&gt;  &lt;p&gt;If we would try to execute this code:&lt;/p&gt;  &lt;blockquote&gt;   &lt;pre class="csharpcode"&gt;var post = session.CreateQuery(&lt;span class="str"&gt;"from Post"&lt;/span&gt;)
    .SetMaxResults(1)
    .UniqueResult&amp;lt;Post&amp;gt;();&lt;/pre&gt;
  &lt;style type="text/css"&gt;&lt;![CDATA[
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;&lt;/blockquote&gt;

&lt;p&gt;You can see from the SQL that NHibernate will load the Text property. In large columns (text, images, etc), the cost of loading a column value is prohibitive, and should be avoided unless absolutely needed.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://ayende.com/Blog/images/ayende_com/Blog/WindowsLiveWriter/NHibernatenewfeatureLazyProperties_10DA6/image_4.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://ayende.com/Blog/images/ayende_com/Blog/WindowsLiveWriter/NHibernatenewfeatureLazyProperties_10DA6/image_thumb_1.png" width="324" height="157" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;This new feature allows you to mark a specific property as lazy, like this:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;property&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;="Text"&lt;/span&gt; &lt;span class="attr"&gt;lazy&lt;/span&gt;&lt;span class="kwrd"&gt;="true"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;
  &lt;style type="text/css"&gt;&lt;![CDATA[
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;&lt;/blockquote&gt;

&lt;p&gt;Once that is done, we can try querying for posts:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;pre class="csharpcode"&gt;var post = session.CreateQuery(&lt;span class="str"&gt;"from Post"&lt;/span&gt;)
    .SetMaxResults(1)
    .UniqueResult&amp;lt;Post&amp;gt;();

System.Console.WriteLine(post.Text);&lt;/pre&gt;
  &lt;style type="text/css"&gt;&lt;![CDATA[
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;&lt;/blockquote&gt;

&lt;p&gt;And the resulting SQL is going to be:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://ayende.com/Blog/images/ayende_com/Blog/WindowsLiveWriter/NHibernatenewfeatureLazyProperties_10DA6/image_6.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://ayende.com/Blog/images/ayende_com/Blog/WindowsLiveWriter/NHibernatenewfeatureLazyProperties_10DA6/image_thumb_2.png" width="316" height="224" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Note that we &lt;em&gt;aren’t&lt;/em&gt; loading the Text property when we query for the post, and if we will inspect the stack trace of the second query we can see it being generated from the Console.WriteLine call.&lt;/p&gt;

&lt;p&gt;But what if we want to query for posts &lt;em&gt;with&lt;/em&gt; their Text property? Doing it this way may very well lead to SELECT N+1 if we need to load all the posts Text properties. NHibernate provide the HQL hint to allow this:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;pre class="csharpcode"&gt;var post = session.CreateQuery(&lt;span class="str"&gt;"from Post fetch all properties"&lt;/span&gt;)
    .SetMaxResults(1)
    .UniqueResult&amp;lt;Post&amp;gt;();

System.Console.WriteLine(post.Text);&lt;/pre&gt;
  &lt;style type="text/css"&gt;&lt;![CDATA[
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;&lt;/blockquote&gt;

&lt;p&gt;Which will result in the following SQL:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://ayende.com/Blog/images/ayende_com/Blog/WindowsLiveWriter/NHibernatenewfeatureLazyProperties_10DA6/image_4.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://ayende.com/Blog/images/ayende_com/Blog/WindowsLiveWriter/NHibernatenewfeatureLazyProperties_10DA6/image_thumb_1.png" width="324" height="157" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;What about multiple lazy properties? NHibernate support them, but you need to keep one thing in mind. NHibernate will load &lt;em&gt;all&lt;/em&gt; the entity’s lazy properties, not just the one that was immediately accessed. By that same token, you can’t eagerly load just some of an entity’s lazy properties from HQL.&lt;/p&gt;

&lt;p&gt;This feature is mostly meant for unique circumstances, such as Person.Image, Post.Text, etc. As usual, be cautious in over using it.&lt;/p&gt;

&lt;p&gt;One last word of caution, this feature is implemented via property interception (and not field interception, like in Hibernate). That was a conscious decision, because we didn’t want to add a bytecode weaving requirement to NHibernate. What this means is that if you mark a property as lazy, it &lt;em&gt;must&lt;/em&gt; be a virtual automatic property. If you attempt to access the underlying field value, instead of going through the property, you will circumvent the lazy loading of the property, and may get unexpected results. &lt;/p&gt;&lt;img src="http://ayende.com/Blog/aggbug/11295.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Ayende Rahien</dc:creator>
            <guid>http://ayende.com/Blog/archive/2010/01/27/nhibernate-new-feature-lazy-properties.aspx</guid>
            <pubDate>Wed, 27 Jan 2010 10:00:00 GMT</pubDate>
            <wfw:comment>http://ayende.com/Blog/comments/11295.aspx</wfw:comment>
            <comments>http://ayende.com/Blog/archive/2010/01/27/nhibernate-new-feature-lazy-properties.aspx#feedback</comments>
            <slash:comments>35</slash:comments>
            <wfw:commentRss>http://ayende.com/Blog/comments/commentRss/11295.aspx</wfw:commentRss>
        </item>
        <item>
            <title>Core NHibernate Course in London, 24th February</title>
            <link>http://ayende.com/Blog/archive/2010/01/21/core-nhibernate-course-in-london-24th-february.aspx</link>
            <description>&lt;p&gt;Well, it is about that time again :-)&lt;/p&gt;  &lt;p&gt;In about a month I’ll be returning to the UK to give another round of my &lt;a href="http://skillsmatter.com/course/open-source-dot-net/core-persistence-with-nhibernate"&gt;NHibernate Course&lt;/a&gt;. It has been a while since I gave that in London, but the previous two runs were &lt;em&gt;very &lt;/em&gt;successful, and I had great time teaching it.&lt;/p&gt;  &lt;p&gt;This course is meant to give you working knowledge how to effectively use NHibernate in your applications, based on real world expertise. &lt;/p&gt;  &lt;p&gt;You can register here: &lt;a href="http://skillsmatter.com/course/open-source-dot-net/core-persistence-with-nhibernate"&gt;http://skillsmatter.com/course/open-source-dot-net/core-persistence-with-nhibernate&lt;/a&gt;&lt;/p&gt;&lt;img src="http://ayende.com/Blog/aggbug/11290.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Ayende Rahien</dc:creator>
            <guid>http://ayende.com/Blog/archive/2010/01/21/core-nhibernate-course-in-london-24th-february.aspx</guid>
            <pubDate>Thu, 21 Jan 2010 12:21:00 GMT</pubDate>
            <wfw:comment>http://ayende.com/Blog/comments/11290.aspx</wfw:comment>
            <comments>http://ayende.com/Blog/archive/2010/01/21/core-nhibernate-course-in-london-24th-february.aspx#feedback</comments>
            <slash:comments>3</slash:comments>
            <wfw:commentRss>http://ayende.com/Blog/comments/commentRss/11290.aspx</wfw:commentRss>
        </item>
        <item>
            <title>Eagerly loading entity associations efficiently with NHibernate</title>
            <link>http://ayende.com/Blog/archive/2010/01/16/eagerly-loading-entity-associations-efficiently-with-nhibernate.aspx</link>
            <description>&lt;p&gt;One of the things that seems to pop up frequently is people wanting to load an entity with all of its associations eagerly. That is pretty easy to do when the associations are many-to-one (that is, there is only one of them for each root entity). Example of those would be things like Owner, Site, etc.&lt;/p&gt;  &lt;p&gt;Here is an HQL query that would load a blog with its owner and site as well:&lt;/p&gt;  &lt;blockquote&gt;   &lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;from&lt;/span&gt; Blog b &lt;span class="kwrd"&gt;left&lt;/span&gt; &lt;span class="kwrd"&gt;join&lt;/span&gt; &lt;span class="kwrd"&gt;fetch&lt;/span&gt; b.Owner &lt;span class="kwrd"&gt;left&lt;/span&gt; &lt;span class="kwrd"&gt;join&lt;/span&gt; &lt;span class="kwrd"&gt;fetch&lt;/span&gt; b.Site&lt;/pre&gt;
  &lt;style type="text/css"&gt;&lt;![CDATA[

.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;&lt;/blockquote&gt;

&lt;p&gt;The problem starts when you try to do the same for multiple &lt;em&gt;collection associations&lt;/em&gt;. NHibernate allows you to do so, but the result is probably not what you would initially expect. This query, for example:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;from&lt;/span&gt; Blog b &lt;span class="kwrd"&gt;left&lt;/span&gt; &lt;span class="kwrd"&gt;join&lt;/span&gt; &lt;span class="kwrd"&gt;fetch&lt;/span&gt; b.Posts &lt;span class="kwrd"&gt;left&lt;/span&gt; &lt;span class="kwrd"&gt;join&lt;/span&gt; &lt;span class="kwrd"&gt;fetch&lt;/span&gt; b.Users &lt;span class="kwrd"&gt;where&lt;/span&gt; b.Id = :id&lt;/pre&gt;
  &lt;style type="text/css"&gt;&lt;![CDATA[

.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;&lt;/blockquote&gt;

&lt;p&gt;Will result in the following SQL statement:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;select&lt;/span&gt; blog0_.Id             &lt;span class="kwrd"&gt;as&lt;/span&gt; Id7_0_,
       posts1_.Id            &lt;span class="kwrd"&gt;as&lt;/span&gt; Id0_1_,
       user3_.Id             &lt;span class="kwrd"&gt;as&lt;/span&gt; Id5_2_,
       blog0_.Title          &lt;span class="kwrd"&gt;as&lt;/span&gt; Title7_0_,
       blog0_.Subtitle       &lt;span class="kwrd"&gt;as&lt;/span&gt; Subtitle7_0_,
       blog0_.AllowsComments &lt;span class="kwrd"&gt;as&lt;/span&gt; AllowsCo4_7_0_,
       blog0_.CreatedAt      &lt;span class="kwrd"&gt;as&lt;/span&gt; CreatedAt7_0_,
       posts1_.Title         &lt;span class="kwrd"&gt;as&lt;/span&gt; Title0_1_,
       posts1_.Text          &lt;span class="kwrd"&gt;as&lt;/span&gt; Text0_1_,
       posts1_.PostedAt      &lt;span class="kwrd"&gt;as&lt;/span&gt; PostedAt0_1_,
       posts1_.BlogId        &lt;span class="kwrd"&gt;as&lt;/span&gt; BlogId0_1_,
       posts1_.UserId        &lt;span class="kwrd"&gt;as&lt;/span&gt; UserId0_1_,
       posts1_.BlogId        &lt;span class="kwrd"&gt;as&lt;/span&gt; BlogId0__,
       posts1_.Id            &lt;span class="kwrd"&gt;as&lt;/span&gt; Id0__,
       user3_.Password       &lt;span class="kwrd"&gt;as&lt;/span&gt; Password5_2_,
       user3_.Username       &lt;span class="kwrd"&gt;as&lt;/span&gt; Username5_2_,
       user3_.Email          &lt;span class="kwrd"&gt;as&lt;/span&gt; Email5_2_,
       user3_.CreatedAt      &lt;span class="kwrd"&gt;as&lt;/span&gt; CreatedAt5_2_,
       user3_.Bio            &lt;span class="kwrd"&gt;as&lt;/span&gt; Bio5_2_,
       users2_.BlogId        &lt;span class="kwrd"&gt;as&lt;/span&gt; BlogId1__,
       users2_.UserId        &lt;span class="kwrd"&gt;as&lt;/span&gt; UserId1__
&lt;span class="kwrd"&gt;from&lt;/span&gt;   Blogs blog0_
       &lt;span class="kwrd"&gt;left&lt;/span&gt; &lt;span class="kwrd"&gt;outer&lt;/span&gt; &lt;span class="kwrd"&gt;join&lt;/span&gt; Posts posts1_
         &lt;span class="kwrd"&gt;on&lt;/span&gt; blog0_.Id = posts1_.BlogId
       &lt;span class="kwrd"&gt;left&lt;/span&gt; &lt;span class="kwrd"&gt;outer&lt;/span&gt; &lt;span class="kwrd"&gt;join&lt;/span&gt; UsersBlogs users2_
         &lt;span class="kwrd"&gt;on&lt;/span&gt; blog0_.Id = users2_.BlogId
       &lt;span class="kwrd"&gt;left&lt;/span&gt; &lt;span class="kwrd"&gt;outer&lt;/span&gt; &lt;span class="kwrd"&gt;join&lt;/span&gt; Users user3_
         &lt;span class="kwrd"&gt;on&lt;/span&gt; users2_.UserId = user3_.Id
&lt;span class="kwrd"&gt;where&lt;/span&gt;  blog0_.Id = 1 /* @p0 */&lt;/pre&gt;
  &lt;style type="text/css"&gt;&lt;![CDATA[

.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;&lt;/blockquote&gt;

&lt;p&gt;Something that may not be apparent immediately is going to result in a Cartesian product. This is pointed out &lt;a href="http://www.nhforge.org/doc/nh/en/index.html#queryhql-joins"&gt;in the documentation&lt;/a&gt;, but I think that we can all agree that while there may be reasons for this behavior, it is far from ideal.&lt;/p&gt;

&lt;p&gt;Let us look at what other OR/Ms are doing, shall we?&lt;/p&gt;

&lt;p&gt;The comparable query using Entity Framework would look like this:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;pre class="csharpcode"&gt;db.Blogs
    .Include(&lt;span class="str"&gt;"Posts"&lt;/span&gt;)
    .Include(&lt;span class="str"&gt;"Users"&lt;/span&gt;)
    .Where(x =&amp;gt; x.Id == i)
    .ToArray();&lt;/pre&gt;
  &lt;style type="text/css"&gt;&lt;![CDATA[

.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;&lt;/blockquote&gt;

&lt;p&gt;And the resulting SQL would be:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;SELECT&lt;/span&gt;   [UnionAll1].[Id]             &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C1],
         [UnionAll1].[Title]          &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C2],
         [UnionAll1].[Subtitle]       &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C3],
         [UnionAll1].[AllowsComments] &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C4],
         [UnionAll1].[CreatedAt]      &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C5],
         [UnionAll1].[C2]             &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C6],
         [UnionAll1].[C1]             &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C7],
         [UnionAll1].[C3]             &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C8],
         [UnionAll1].[Id1]            &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C9],
         [UnionAll1].[Title1]         &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C10],
         [UnionAll1].[Text]           &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C11],
         [UnionAll1].[PostedAt]       &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C12],
         [UnionAll1].[BlogId]         &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C13],
         [UnionAll1].[UserId]         &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C14],
         [UnionAll1].[C4]             &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C15],
         [UnionAll1].[C5]             &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C16],
         [UnionAll1].[C6]             &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C17],
         [UnionAll1].[C7]             &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C18],
         [UnionAll1].[C8]             &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C19],
         [UnionAll1].[C9]             &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C20]
&lt;span class="kwrd"&gt;FROM&lt;/span&gt;     (&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; &lt;span class="kwrd"&gt;CASE&lt;/span&gt; 
                   &lt;span class="kwrd"&gt;WHEN&lt;/span&gt; ([Extent2].[Id] &lt;span class="kwrd"&gt;IS&lt;/span&gt; &lt;span class="kwrd"&gt;NULL&lt;/span&gt;) &lt;span class="kwrd"&gt;THEN&lt;/span&gt; &lt;span class="kwrd"&gt;CAST&lt;/span&gt;(&lt;span class="kwrd"&gt;NULL&lt;/span&gt; &lt;span class="kwrd"&gt;AS&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt;)
                   &lt;span class="kwrd"&gt;ELSE&lt;/span&gt; 1
                 &lt;span class="kwrd"&gt;END&lt;/span&gt; &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C1],
                 [Extent1].[Id]             &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Id],
                 [Extent1].[Title]          &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Title],
                 [Extent1].[Subtitle]       &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Subtitle],
                 [Extent1].[AllowsComments] &lt;span class="kwrd"&gt;AS&lt;/span&gt; [AllowsComments],
                 [Extent1].[CreatedAt]      &lt;span class="kwrd"&gt;AS&lt;/span&gt; [CreatedAt],
                 1                          &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C2],
                 &lt;span class="kwrd"&gt;CASE&lt;/span&gt; 
                   &lt;span class="kwrd"&gt;WHEN&lt;/span&gt; ([Extent2].[Id] &lt;span class="kwrd"&gt;IS&lt;/span&gt; &lt;span class="kwrd"&gt;NULL&lt;/span&gt;) &lt;span class="kwrd"&gt;THEN&lt;/span&gt; &lt;span class="kwrd"&gt;CAST&lt;/span&gt;(&lt;span class="kwrd"&gt;NULL&lt;/span&gt; &lt;span class="kwrd"&gt;AS&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt;)
                   &lt;span class="kwrd"&gt;ELSE&lt;/span&gt; 1
                 &lt;span class="kwrd"&gt;END&lt;/span&gt; &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C3],
                 [Extent2].[Id]             &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Id1],
                 [Extent2].[Title]          &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Title1],
                 [Extent2].[Text]           &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Text],
                 [Extent2].[PostedAt]       &lt;span class="kwrd"&gt;AS&lt;/span&gt; [PostedAt],
                 [Extent2].[BlogId]         &lt;span class="kwrd"&gt;AS&lt;/span&gt; [BlogId],
                 [Extent2].[UserId]         &lt;span class="kwrd"&gt;AS&lt;/span&gt; [UserId],
                 &lt;span class="kwrd"&gt;CAST&lt;/span&gt;(&lt;span class="kwrd"&gt;NULL&lt;/span&gt; &lt;span class="kwrd"&gt;AS&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt;)          &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C4],
                 &lt;span class="kwrd"&gt;CAST&lt;/span&gt;(&lt;span class="kwrd"&gt;NULL&lt;/span&gt; &lt;span class="kwrd"&gt;AS&lt;/span&gt; varbinary(1)) &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C5],
                 &lt;span class="kwrd"&gt;CAST&lt;/span&gt;(&lt;span class="kwrd"&gt;NULL&lt;/span&gt; &lt;span class="kwrd"&gt;AS&lt;/span&gt; &lt;span class="kwrd"&gt;varchar&lt;/span&gt;(1))   &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C6],
                 &lt;span class="kwrd"&gt;CAST&lt;/span&gt;(&lt;span class="kwrd"&gt;NULL&lt;/span&gt; &lt;span class="kwrd"&gt;AS&lt;/span&gt; &lt;span class="kwrd"&gt;varchar&lt;/span&gt;(1))   &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C7],
                 &lt;span class="kwrd"&gt;CAST&lt;/span&gt;(&lt;span class="kwrd"&gt;NULL&lt;/span&gt; &lt;span class="kwrd"&gt;AS&lt;/span&gt; datetime)     &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C8],
                 &lt;span class="kwrd"&gt;CAST&lt;/span&gt;(&lt;span class="kwrd"&gt;NULL&lt;/span&gt; &lt;span class="kwrd"&gt;AS&lt;/span&gt; &lt;span class="kwrd"&gt;varchar&lt;/span&gt;(1))   &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C9]
          &lt;span class="kwrd"&gt;FROM&lt;/span&gt;   [dbo].[Blogs] &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Extent1]
                 &lt;span class="kwrd"&gt;LEFT&lt;/span&gt; &lt;span class="kwrd"&gt;OUTER&lt;/span&gt; &lt;span class="kwrd"&gt;JOIN&lt;/span&gt; [dbo].[Posts] &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Extent2]
                   &lt;span class="kwrd"&gt;ON&lt;/span&gt; [Extent1].[Id] = [Extent2].[BlogId]
          &lt;span class="kwrd"&gt;WHERE&lt;/span&gt;  [Extent1].[Id] = 1 /* @p__linq__1 */
          &lt;span class="kwrd"&gt;UNION&lt;/span&gt; &lt;span class="kwrd"&gt;ALL&lt;/span&gt;
          &lt;span class="kwrd"&gt;SELECT&lt;/span&gt; 2                          &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C1],
                 [Extent3].[Id]             &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Id],
                 [Extent3].[Title]          &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Title],
                 [Extent3].[Subtitle]       &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Subtitle],
                 [Extent3].[AllowsComments] &lt;span class="kwrd"&gt;AS&lt;/span&gt; [AllowsComments],
                 [Extent3].[CreatedAt]      &lt;span class="kwrd"&gt;AS&lt;/span&gt; [CreatedAt],
                 1                          &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C2],
                 &lt;span class="kwrd"&gt;CAST&lt;/span&gt;(&lt;span class="kwrd"&gt;NULL&lt;/span&gt; &lt;span class="kwrd"&gt;AS&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt;)          &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C3],
                 &lt;span class="kwrd"&gt;CAST&lt;/span&gt;(&lt;span class="kwrd"&gt;NULL&lt;/span&gt; &lt;span class="kwrd"&gt;AS&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt;)          &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C4],
                 &lt;span class="kwrd"&gt;CAST&lt;/span&gt;(&lt;span class="kwrd"&gt;NULL&lt;/span&gt; &lt;span class="kwrd"&gt;AS&lt;/span&gt; &lt;span class="kwrd"&gt;varchar&lt;/span&gt;(1))   &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C5],
                 &lt;span class="kwrd"&gt;CAST&lt;/span&gt;(&lt;span class="kwrd"&gt;NULL&lt;/span&gt; &lt;span class="kwrd"&gt;AS&lt;/span&gt; &lt;span class="kwrd"&gt;varchar&lt;/span&gt;(1))   &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C6],
                 &lt;span class="kwrd"&gt;CAST&lt;/span&gt;(&lt;span class="kwrd"&gt;NULL&lt;/span&gt; &lt;span class="kwrd"&gt;AS&lt;/span&gt; datetime)     &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C7],
                 &lt;span class="kwrd"&gt;CAST&lt;/span&gt;(&lt;span class="kwrd"&gt;NULL&lt;/span&gt; &lt;span class="kwrd"&gt;AS&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt;)          &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C8],
                 &lt;span class="kwrd"&gt;CAST&lt;/span&gt;(&lt;span class="kwrd"&gt;NULL&lt;/span&gt; &lt;span class="kwrd"&gt;AS&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt;)          &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C9],
                 [Join2].[Id]               &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Id1],
                 [Join2].[Password]         &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Password],
                 [Join2].[Username]         &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Username],
                 [Join2].[Email]            &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Email],
                 [Join2].[CreatedAt]        &lt;span class="kwrd"&gt;AS&lt;/span&gt; [CreatedAt1],
                 [Join2].[Bio]              &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Bio]
          &lt;span class="kwrd"&gt;FROM&lt;/span&gt;   [dbo].[Blogs] &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Extent3]
                 &lt;span class="kwrd"&gt;INNER&lt;/span&gt; &lt;span class="kwrd"&gt;JOIN&lt;/span&gt; (&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; [Extent4].[UserId]    &lt;span class="kwrd"&gt;AS&lt;/span&gt; [UserId],
                                    [Extent4].[BlogId]    &lt;span class="kwrd"&gt;AS&lt;/span&gt; [BlogId],
                                    [Extent5].[Id]        &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Id],
                                    [Extent5].[Password]  &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Password],
                                    [Extent5].[Username]  &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Username],
                                    [Extent5].[Email]     &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Email],
                                    [Extent5].[CreatedAt] &lt;span class="kwrd"&gt;AS&lt;/span&gt; [CreatedAt],
                                    [Extent5].[Bio]       &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Bio]
                             &lt;span class="kwrd"&gt;FROM&lt;/span&gt;   [dbo].[UsersBlogs] &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Extent4]
                                    &lt;span class="kwrd"&gt;INNER&lt;/span&gt; &lt;span class="kwrd"&gt;JOIN&lt;/span&gt; [dbo].[Users] &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Extent5]
                                      &lt;span class="kwrd"&gt;ON&lt;/span&gt; [Extent5].[Id] = [Extent4].[UserId]) &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Join2]
                   &lt;span class="kwrd"&gt;ON&lt;/span&gt; [Extent3].[Id] = [Join2].[BlogId]
          &lt;span class="kwrd"&gt;WHERE&lt;/span&gt;  [Extent3].[Id] = 1 /* @p__linq__1 */) &lt;span class="kwrd"&gt;AS&lt;/span&gt; [UnionAll1]
&lt;span class="kwrd"&gt;ORDER&lt;/span&gt; &lt;span class="kwrd"&gt;BY&lt;/span&gt; [UnionAll1].[Id] &lt;span class="kwrd"&gt;ASC&lt;/span&gt;,
         [UnionAll1].[C1] ASC&lt;/pre&gt;
  &lt;style type="text/css"&gt;&lt;![CDATA[

.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;&lt;/blockquote&gt;

&lt;p&gt;At this point, I am pretty sure, your eyes shut down in self defense. This is one complex query. But, basically, this is a complex query because EF is executing the following two queries and unioning them.&lt;/p&gt;

&lt;p&gt;Eager load Blog Posts:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; &lt;span class="kwrd"&gt;CASE&lt;/span&gt; 
       &lt;span class="kwrd"&gt;WHEN&lt;/span&gt; ([Extent2].[Id] &lt;span class="kwrd"&gt;IS&lt;/span&gt; &lt;span class="kwrd"&gt;NULL&lt;/span&gt;) &lt;span class="kwrd"&gt;THEN&lt;/span&gt; &lt;span class="kwrd"&gt;CAST&lt;/span&gt;(&lt;span class="kwrd"&gt;NULL&lt;/span&gt; &lt;span class="kwrd"&gt;AS&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt;)
       &lt;span class="kwrd"&gt;ELSE&lt;/span&gt; 1
     &lt;span class="kwrd"&gt;END&lt;/span&gt; &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C1],
     [Extent1].[Id]             &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Id],
     [Extent1].[Title]          &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Title],
     [Extent1].[Subtitle]       &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Subtitle],
     [Extent1].[AllowsComments] &lt;span class="kwrd"&gt;AS&lt;/span&gt; [AllowsComments],
     [Extent1].[CreatedAt]      &lt;span class="kwrd"&gt;AS&lt;/span&gt; [CreatedAt],
     1                          &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C2],
     &lt;span class="kwrd"&gt;CASE&lt;/span&gt; 
       &lt;span class="kwrd"&gt;WHEN&lt;/span&gt; ([Extent2].[Id] &lt;span class="kwrd"&gt;IS&lt;/span&gt; &lt;span class="kwrd"&gt;NULL&lt;/span&gt;) &lt;span class="kwrd"&gt;THEN&lt;/span&gt; &lt;span class="kwrd"&gt;CAST&lt;/span&gt;(&lt;span class="kwrd"&gt;NULL&lt;/span&gt; &lt;span class="kwrd"&gt;AS&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt;)
       &lt;span class="kwrd"&gt;ELSE&lt;/span&gt; 1
     &lt;span class="kwrd"&gt;END&lt;/span&gt; &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C3],
     [Extent2].[Id]             &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Id1],
     [Extent2].[Title]          &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Title1],
     [Extent2].[Text]           &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Text],
     [Extent2].[PostedAt]       &lt;span class="kwrd"&gt;AS&lt;/span&gt; [PostedAt],
     [Extent2].[BlogId]         &lt;span class="kwrd"&gt;AS&lt;/span&gt; [BlogId],
     [Extent2].[UserId]         &lt;span class="kwrd"&gt;AS&lt;/span&gt; [UserId],
     &lt;span class="kwrd"&gt;CAST&lt;/span&gt;(&lt;span class="kwrd"&gt;NULL&lt;/span&gt; &lt;span class="kwrd"&gt;AS&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt;)          &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C4],
     &lt;span class="kwrd"&gt;CAST&lt;/span&gt;(&lt;span class="kwrd"&gt;NULL&lt;/span&gt; &lt;span class="kwrd"&gt;AS&lt;/span&gt; varbinary(1)) &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C5],
     &lt;span class="kwrd"&gt;CAST&lt;/span&gt;(&lt;span class="kwrd"&gt;NULL&lt;/span&gt; &lt;span class="kwrd"&gt;AS&lt;/span&gt; &lt;span class="kwrd"&gt;varchar&lt;/span&gt;(1))   &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C6],
     &lt;span class="kwrd"&gt;CAST&lt;/span&gt;(&lt;span class="kwrd"&gt;NULL&lt;/span&gt; &lt;span class="kwrd"&gt;AS&lt;/span&gt; &lt;span class="kwrd"&gt;varchar&lt;/span&gt;(1))   &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C7],
     &lt;span class="kwrd"&gt;CAST&lt;/span&gt;(&lt;span class="kwrd"&gt;NULL&lt;/span&gt; &lt;span class="kwrd"&gt;AS&lt;/span&gt; datetime)     &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C8],
     &lt;span class="kwrd"&gt;CAST&lt;/span&gt;(&lt;span class="kwrd"&gt;NULL&lt;/span&gt; &lt;span class="kwrd"&gt;AS&lt;/span&gt; &lt;span class="kwrd"&gt;varchar&lt;/span&gt;(1))   &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C9]
&lt;span class="kwrd"&gt;FROM&lt;/span&gt;   [dbo].[Blogs] &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Extent1]
     &lt;span class="kwrd"&gt;LEFT&lt;/span&gt; &lt;span class="kwrd"&gt;OUTER&lt;/span&gt; &lt;span class="kwrd"&gt;JOIN&lt;/span&gt; [dbo].[Posts] &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Extent2]
       &lt;span class="kwrd"&gt;ON&lt;/span&gt; [Extent1].[Id] = [Extent2].[BlogId]
&lt;span class="kwrd"&gt;WHERE&lt;/span&gt;  [Extent1].[Id] = 1 /* @p__linq__1 */&lt;/pre&gt;
  &lt;style type="text/css"&gt;&lt;![CDATA[

.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;&lt;/blockquote&gt;

&lt;p&gt;Eager load Blog Users:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; 2                          &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C1],
     [Extent3].[Id]             &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Id],
     [Extent3].[Title]          &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Title],
     [Extent3].[Subtitle]       &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Subtitle],
     [Extent3].[AllowsComments] &lt;span class="kwrd"&gt;AS&lt;/span&gt; [AllowsComments],
     [Extent3].[CreatedAt]      &lt;span class="kwrd"&gt;AS&lt;/span&gt; [CreatedAt],
     1                          &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C2],
     &lt;span class="kwrd"&gt;CAST&lt;/span&gt;(&lt;span class="kwrd"&gt;NULL&lt;/span&gt; &lt;span class="kwrd"&gt;AS&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt;)          &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C3],
     &lt;span class="kwrd"&gt;CAST&lt;/span&gt;(&lt;span class="kwrd"&gt;NULL&lt;/span&gt; &lt;span class="kwrd"&gt;AS&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt;)          &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C4],
     &lt;span class="kwrd"&gt;CAST&lt;/span&gt;(&lt;span class="kwrd"&gt;NULL&lt;/span&gt; &lt;span class="kwrd"&gt;AS&lt;/span&gt; &lt;span class="kwrd"&gt;varchar&lt;/span&gt;(1))   &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C5],
     &lt;span class="kwrd"&gt;CAST&lt;/span&gt;(&lt;span class="kwrd"&gt;NULL&lt;/span&gt; &lt;span class="kwrd"&gt;AS&lt;/span&gt; &lt;span class="kwrd"&gt;varchar&lt;/span&gt;(1))   &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C6],
     &lt;span class="kwrd"&gt;CAST&lt;/span&gt;(&lt;span class="kwrd"&gt;NULL&lt;/span&gt; &lt;span class="kwrd"&gt;AS&lt;/span&gt; datetime)     &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C7],
     &lt;span class="kwrd"&gt;CAST&lt;/span&gt;(&lt;span class="kwrd"&gt;NULL&lt;/span&gt; &lt;span class="kwrd"&gt;AS&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt;)          &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C8],
     &lt;span class="kwrd"&gt;CAST&lt;/span&gt;(&lt;span class="kwrd"&gt;NULL&lt;/span&gt; &lt;span class="kwrd"&gt;AS&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt;)          &lt;span class="kwrd"&gt;AS&lt;/span&gt; [C9],
     [Join2].[Id]               &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Id1],
     [Join2].[Password]         &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Password],
     [Join2].[Username]         &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Username],
     [Join2].[Email]            &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Email],
     [Join2].[CreatedAt]        &lt;span class="kwrd"&gt;AS&lt;/span&gt; [CreatedAt1],
     [Join2].[Bio]              &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Bio]
&lt;span class="kwrd"&gt;FROM&lt;/span&gt;   [dbo].[Blogs] &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Extent3]
     &lt;span class="kwrd"&gt;INNER&lt;/span&gt; &lt;span class="kwrd"&gt;JOIN&lt;/span&gt; (&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; [Extent4].[UserId]    &lt;span class="kwrd"&gt;AS&lt;/span&gt; [UserId],
                        [Extent4].[BlogId]    &lt;span class="kwrd"&gt;AS&lt;/span&gt; [BlogId],
                        [Extent5].[Id]        &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Id],
                        [Extent5].[Password]  &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Password],
                        [Extent5].[Username]  &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Username],
                        [Extent5].[Email]     &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Email],
                        [Extent5].[CreatedAt] &lt;span class="kwrd"&gt;AS&lt;/span&gt; [CreatedAt],
                        [Extent5].[Bio]       &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Bio]
                 &lt;span class="kwrd"&gt;FROM&lt;/span&gt;   [dbo].[UsersBlogs] &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Extent4]
                        &lt;span class="kwrd"&gt;INNER&lt;/span&gt; &lt;span class="kwrd"&gt;JOIN&lt;/span&gt; [dbo].[Users] &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Extent5]
                          &lt;span class="kwrd"&gt;ON&lt;/span&gt; [Extent5].[Id] = [Extent4].[UserId]) &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Join2]
       &lt;span class="kwrd"&gt;ON&lt;/span&gt; [Extent3].[Id] = [Join2].[BlogId]
&lt;span class="kwrd"&gt;WHERE&lt;/span&gt;  [Extent3].[Id] = 1 /* @p__linq__1 */) &lt;/pre&gt;
  &lt;style type="text/css"&gt;&lt;![CDATA[

.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;&lt;/blockquote&gt;

&lt;p&gt;The query may be complex, but it get the job done and does so without bothering the us much. The question is, can we do the same with NHibernate?&lt;/p&gt;

&lt;p&gt;As it run out, we can, pretty easily too. The following code will do just that:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;pre class="csharpcode"&gt; var blogs = s.CreateQuery(&lt;span class="str"&gt;"from Blog b left join fetch b.Posts where b.Id = :id"&lt;/span&gt;)
     .SetParameter(&lt;span class="str"&gt;"id"&lt;/span&gt;, 1)
     .Future&amp;lt;Blog&amp;gt;();

 s.CreateQuery(&lt;span class="str"&gt;"from Blog b left join fetch b.Users where b.Id = :id1"&lt;/span&gt;)
     .SetParameter(&lt;span class="str"&gt;"id1"&lt;/span&gt;, 1)
     .Future&amp;lt;Blog&amp;gt;();&lt;/pre&gt;
  &lt;style type="text/css"&gt;&lt;![CDATA[

.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;&lt;/blockquote&gt;

&lt;p&gt;So, what is going on here? We are actually issuing &lt;em&gt;two &lt;/em&gt;queries, each of them to eagerly load a &lt;em&gt;single&lt;/em&gt; collection. The trick is that we are using &lt;em&gt;future queries&lt;/em&gt; to do so. That means that the query that is going to be sent to the database to get those results is:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;select&lt;/span&gt; blog0_.Id             &lt;span class="kwrd"&gt;as&lt;/span&gt; Id7_0_,
       posts1_.Id            &lt;span class="kwrd"&gt;as&lt;/span&gt; Id0_1_,
       blog0_.Title          &lt;span class="kwrd"&gt;as&lt;/span&gt; Title7_0_,
       blog0_.Subtitle       &lt;span class="kwrd"&gt;as&lt;/span&gt; Subtitle7_0_,
       blog0_.AllowsComments &lt;span class="kwrd"&gt;as&lt;/span&gt; AllowsCo4_7_0_,
       blog0_.CreatedAt      &lt;span class="kwrd"&gt;as&lt;/span&gt; CreatedAt7_0_,
       posts1_.Title         &lt;span class="kwrd"&gt;as&lt;/span&gt; Title0_1_,
       posts1_.Text          &lt;span class="kwrd"&gt;as&lt;/span&gt; Text0_1_,
       posts1_.PostedAt      &lt;span class="kwrd"&gt;as&lt;/span&gt; PostedAt0_1_,
       posts1_.BlogId        &lt;span class="kwrd"&gt;as&lt;/span&gt; BlogId0_1_,
       posts1_.UserId        &lt;span class="kwrd"&gt;as&lt;/span&gt; UserId0_1_,
       posts1_.BlogId        &lt;span class="kwrd"&gt;as&lt;/span&gt; BlogId0__,
       posts1_.Id            &lt;span class="kwrd"&gt;as&lt;/span&gt; Id0__
&lt;span class="kwrd"&gt;from&lt;/span&gt;   Blogs blog0_
       &lt;span class="kwrd"&gt;left&lt;/span&gt; &lt;span class="kwrd"&gt;outer&lt;/span&gt; &lt;span class="kwrd"&gt;join&lt;/span&gt; Posts posts1_
         &lt;span class="kwrd"&gt;on&lt;/span&gt; blog0_.Id = posts1_.BlogId
&lt;span class="kwrd"&gt;where&lt;/span&gt;  blog0_.Id = 1 /* @p0 */
&lt;span class="kwrd"&gt;select&lt;/span&gt; blog0_.Id             &lt;span class="kwrd"&gt;as&lt;/span&gt; Id7_0_,
       user2_.Id             &lt;span class="kwrd"&gt;as&lt;/span&gt; Id5_1_,
       blog0_.Title          &lt;span class="kwrd"&gt;as&lt;/span&gt; Title7_0_,
       blog0_.Subtitle       &lt;span class="kwrd"&gt;as&lt;/span&gt; Subtitle7_0_,
       blog0_.AllowsComments &lt;span class="kwrd"&gt;as&lt;/span&gt; AllowsCo4_7_0_,
       blog0_.CreatedAt      &lt;span class="kwrd"&gt;as&lt;/span&gt; CreatedAt7_0_,
       user2_.Password       &lt;span class="kwrd"&gt;as&lt;/span&gt; Password5_1_,
       user2_.Username       &lt;span class="kwrd"&gt;as&lt;/span&gt; Username5_1_,
       user2_.Email          &lt;span class="kwrd"&gt;as&lt;/span&gt; Email5_1_,
       user2_.CreatedAt      &lt;span class="kwrd"&gt;as&lt;/span&gt; CreatedAt5_1_,
       user2_.Bio            &lt;span class="kwrd"&gt;as&lt;/span&gt; Bio5_1_,
       users1_.BlogId        &lt;span class="kwrd"&gt;as&lt;/span&gt; BlogId0__,
       users1_.UserId        &lt;span class="kwrd"&gt;as&lt;/span&gt; UserId0__
&lt;span class="kwrd"&gt;from&lt;/span&gt;   Blogs blog0_
       &lt;span class="kwrd"&gt;left&lt;/span&gt; &lt;span class="kwrd"&gt;outer&lt;/span&gt; &lt;span class="kwrd"&gt;join&lt;/span&gt; UsersBlogs users1_
         &lt;span class="kwrd"&gt;on&lt;/span&gt; blog0_.Id = users1_.BlogId
       &lt;span class="kwrd"&gt;left&lt;/span&gt; &lt;span class="kwrd"&gt;outer&lt;/span&gt; &lt;span class="kwrd"&gt;join&lt;/span&gt; Users user2_
         &lt;span class="kwrd"&gt;on&lt;/span&gt; users1_.UserId = user2_.Id
&lt;span class="kwrd"&gt;where&lt;/span&gt;  blog0_.Id = 1 /* @p1 */&lt;/pre&gt;
  &lt;style type="text/css"&gt;&lt;![CDATA[

.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;&lt;/blockquote&gt;

&lt;p&gt;Note that we are essentially discarding the results of the second query. The reason for that is that we aren’t actually interested in those results, we execute this query solely to get NHibernate to fill the Users’ collection of the relevant entity.&lt;/p&gt;

&lt;p&gt;I generally use this method so I would have the first query to eager load all the many-to-one associations, and then a series of queries (one per required loaded collection) to load one-to-many or many-to-many associations.&lt;/p&gt;&lt;img src="http://ayende.com/Blog/aggbug/11285.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Ayende Rahien</dc:creator>
            <guid>http://ayende.com/Blog/archive/2010/01/16/eagerly-loading-entity-associations-efficiently-with-nhibernate.aspx</guid>
            <pubDate>Sat, 16 Jan 2010 10:00:00 GMT</pubDate>
            <wfw:comment>http://ayende.com/Blog/comments/11285.aspx</wfw:comment>
            <comments>http://ayende.com/Blog/archive/2010/01/16/eagerly-loading-entity-associations-efficiently-with-nhibernate.aspx#feedback</comments>
            <slash:comments>26</slash:comments>
            <wfw:commentRss>http://ayende.com/Blog/comments/commentRss/11285.aspx</wfw:commentRss>
        </item>
        <item>
            <title>NHibernate, polymorphic associations and ghost objects</title>
            <link>http://ayende.com/Blog/archive/2010/01/09/nhibernate-polymorphic-associations-and-ghost-objects.aspx</link>
            <description>&lt;p&gt;&lt;a href="http://ayende.com/Blog/images/ayende_com/Blog/WindowsLiveWriter/NHibernatepolymorphicassociationsandghos_AB95/image_2.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; margin-left: 0px; border-top: 0px; margin-right: 0px; border-right: 0px" title="image" border="0" alt="image" align="right" src="http://ayende.com/Blog/images/ayende_com/Blog/WindowsLiveWriter/NHibernatepolymorphicassociationsandghos_AB95/image_thumb.png" width="427" height="205" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;One of the more interesting points of &lt;a href="http://ayende.com/Blog/archive/2009/12/25/responding-to-how-ef-is-better-nh-commentaryhellip.aspx"&gt;my&lt;/a&gt; &lt;a href="http://ayende.com/Blog/archive/2009/12/24/what-can-ef-4.0-do-that-nhibernate-canrsquot.aspx"&gt;posts&lt;/a&gt; about Entity Framework &amp;amp; NHibernate is the discovery of things that Entity Framework can do that NHibernate cannot. In fact, if you’ll read the posts, instead of the comments, you can see that this is precisely what I asked, but people didn’t really read the text.&lt;/p&gt;  &lt;p&gt;I wanted to dedicate this post to ghost objects, and how NHibernate deals with them.&lt;/p&gt;  &lt;p&gt;Before we start, let me explain what ghost objects &lt;em&gt;are&lt;/em&gt;. Let us say that you have a many to one polymorphic association, such as the one represented as Comment.Post.&lt;/p&gt;  &lt;p&gt;A post may be either a Post or an Article, and since NHibernate by default will lazy load the association, NHibernate will generate a proxy object (also called a ghost object). That, in turn, result in several common issues: Leaking this and the inability to cast to the proper type are the most common ones.&lt;/p&gt;  &lt;p&gt;In practice, this is something that you would generally run into when you are violating the Liskov Substitution Principle, so my general recommendation is to just fix your design.&lt;/p&gt;  &lt;p&gt;Nevertheless, since the question pop up occasionally, I thought that I might write a bit more details on how to resolve this. Basically, the main issue is that at the point in time where we are loading the Comment entity, we don’t have enough information to know what the actual entity type &lt;em&gt;is&lt;/em&gt;. The simplest way to work around this issue is to tell NHibernate to load the associated entity as part of the parent entity load.&lt;/p&gt;  &lt;p&gt;In the case of the comment, we can do it like this:&lt;/p&gt;  &lt;blockquote&gt;   &lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;many-to-one&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;="Post"&lt;/span&gt; 
             &lt;span class="attr"&gt;lazy&lt;/span&gt;&lt;span class="kwrd"&gt;="false"&lt;/span&gt;
             &lt;span class="attr"&gt;outer-join&lt;/span&gt;&lt;span class="kwrd"&gt;="true"&lt;/span&gt;
             &lt;span class="attr"&gt;column&lt;/span&gt;&lt;span class="kwrd"&gt;="PostId"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;
  &lt;style type="text/css"&gt;&lt;![CDATA[
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;&lt;/blockquote&gt;

&lt;p&gt;The lazy=”false” tell NHibernate to load the association eagerly, while the outer-join will add a join to load it in a single query. One thing to note, however, is that (by design) HQL queries will ignore any hints in the mapping, so you would have to specify join fetch explicitly in the mapping, otherwise it would generate a separate query for that.&lt;/p&gt;

&lt;p&gt;Since we eagerly load the associated entity, and we know its type, we don’t have to deal with any proxies, and can avoid the ghost objects problem completely.&lt;/p&gt;&lt;img src="http://ayende.com/Blog/aggbug/11272.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Ayende Rahien</dc:creator>
            <guid>http://ayende.com/Blog/archive/2010/01/09/nhibernate-polymorphic-associations-and-ghost-objects.aspx</guid>
            <pubDate>Sat, 09 Jan 2010 10:00:00 GMT</pubDate>
            <comments>http://ayende.com/Blog/archive/2010/01/09/nhibernate-polymorphic-associations-and-ghost-objects.aspx#feedback</comments>
            <slash:comments>25</slash:comments>
            <wfw:commentRss>http://ayende.com/Blog/comments/commentRss/11272.aspx</wfw:commentRss>
        </item>
        <item>
            <title>Responding to Effectus commentary</title>
            <link>http://ayende.com/Blog/archive/2010/01/08/responding-to-effectus-commentary.aspx</link>
            <description>&lt;p&gt;Jose was kind enough to &lt;a href="http://jfromaniello.blogspot.com/2009/12/reviewing-and-changing-effectus.html"&gt;post a review&lt;/a&gt; of my sample Effectus application. This post is a reply to that.&lt;/p&gt;  &lt;p&gt;The first thing that Jose didn’t like was the fact that I didn’t put an abstraction layer in front of NHibernate’s usage in my application. There are several reasons for that, the first, and simplest, is that I was trying to explain how to use NHibernate, and for that, I wanted to deal with the lowest common denominator, not show off a particular wrapper implementation. Showing NHibernate usage directly means that even if you are using another library with it, you would still be able to take advantage of the information that I give you.&lt;/p&gt;  &lt;p&gt;Second, and quite important, is that by using NHibernate directly I can take advantage of NHibernate features explicitly meant to support certain scenarios, but which are not likely to be expose when libraries wrap NHibernate. A good example of that is in Jose’s sample code, which make use of an ISession instead of IStatelessSession to load the data for the main screen. As explained in the article, the difference between the two is &lt;em&gt;important&lt;/em&gt;, and in the context where it is used it introduce what is effectively a memory leak into Jose’s implementation, as well as the chance for some really interesting errors down the road if the session will run into an error.&lt;/p&gt;  &lt;p&gt;Third, Jose bring up the following:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;This presenter is &lt;strong&gt;&lt;u&gt;GLUED&lt;/u&gt;&lt;/strong&gt; to NHibernate. And this means for instance that you can’t test it without NHibernate, and this means that you can’t change your persistence layer.&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Yes, and that is intentional. That is the very &lt;em&gt;task&lt;/em&gt; that those presenters are &lt;em&gt;for&lt;/em&gt;. Trying to abstract that away just means that I put an additional layer of abstraction that does absolutely nothing. For that matter, let us look at Jose’s implementation using an abstraction layer compared to mine.&lt;/p&gt;  &lt;p&gt;Here is my implementation:&lt;/p&gt;  &lt;blockquote&gt;   &lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; LoadPage(&lt;span class="kwrd"&gt;int&lt;/span&gt; page)
{
    &lt;span class="kwrd"&gt;using&lt;/span&gt; (var tx = StatelessSession.BeginTransaction())
    {
        var actions = StatelessSession.CreateCriteria&amp;lt;ToDoAction&amp;gt;()
            .SetFirstResult(page * PageSize)
            .SetMaxResults(PageSize)
            .List&amp;lt;ToDoAction&amp;gt;();

        var total = StatelessSession.CreateCriteria&amp;lt;ToDoAction&amp;gt;()
            .SetProjection(Projections.RowCount())
            .UniqueResult&amp;lt;&lt;span class="kwrd"&gt;int&lt;/span&gt;&amp;gt;();

        &lt;span class="kwrd"&gt;this&lt;/span&gt;.NumberOfPages.Value = total / PageSize + (total % PageSize == 0 ? 0 : 1);
        &lt;span class="kwrd"&gt;this&lt;/span&gt;.Model = &lt;span class="kwrd"&gt;new&lt;/span&gt; Model
        {
            Actions = &lt;span class="kwrd"&gt;new&lt;/span&gt; ObservableCollection&amp;lt;ToDoAction&amp;gt;(actions),
            NumberOfPages = NumberOfPages,
            CurrentPage = CurrentPage + 1
        };
        &lt;span class="kwrd"&gt;this&lt;/span&gt;.CurrentPage.Value = page;

        tx.Commit();
    }
}&lt;/pre&gt;
  &lt;style type="text/css"&gt;&lt;![CDATA[
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;&lt;/blockquote&gt;

&lt;p&gt;And here is Jose’s:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; LoadPage(&lt;span class="kwrd"&gt;int&lt;/span&gt; page)
{
    var actions = _toDoActionsDao.RetrieveAll()
                                 .Skip(page * PageSize)
                                 .Take(PageSize).ToList();

    var total = _toDoActionsDao.RetrieveAll().Count();
    
    NumberOfPages.Value = total / PageSize 
                        + (total % PageSize == 0 ? 0 : 1);
    
    Model = &lt;span class="kwrd"&gt;new&lt;/span&gt; Model
    {
        Actions = &lt;span class="kwrd"&gt;new&lt;/span&gt; ObservableCollection&amp;lt;ToDoAction&amp;gt;(actions),
        NumberOfPages = NumberOfPages,
        CurrentPage = CurrentPage + 1
    };
    
    CurrentPage.Value = page;
}&lt;/pre&gt;
  &lt;style type="text/css"&gt;&lt;![CDATA[
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;&lt;/blockquote&gt;

&lt;p&gt;The code is &lt;em&gt;still&lt;/em&gt; doing the exact same thing, in other words, we haven’t moved any logic away, and most of the code is dealing with data access details. My approach for testing this is to make use of an in memory database, which result in a single test that make sure that the code works, instead of a series of tests that verifies that each piece work independently and then my single test. I find it much more effective in terms of time &amp;amp; effort.&lt;/p&gt;

&lt;p&gt;As for the idea of changing your persistence layer, forget about it. It isn’t going to happen in any real world application without a lot of effort, so you might as well save yourself the effort of working with the lowest common denominator and take full advantage of the framework. Just to note, when I ported the &lt;a href="http://NerdDinner.com"&gt;NerdDinner&lt;/a&gt; code to NHibernate (an application that make use of a &lt;em&gt;single&lt;/em&gt; table), I had to make major changes to the application.&lt;/p&gt;

&lt;p&gt;Jose didn’t like this method:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; OnSave()
{
    &lt;span class="kwrd"&gt;bool&lt;/span&gt; successfulSave;
    &lt;span class="kwrd"&gt;try&lt;/span&gt;
    {
        &lt;span class="kwrd"&gt;using&lt;/span&gt; (var tx = Session.BeginTransaction())
        {
            &lt;span class="rem"&gt;// this isn't strictly necessary, NHibernate will &lt;/span&gt;
            &lt;span class="rem"&gt;// automatically do it for us, but it make things&lt;/span&gt;
            &lt;span class="rem"&gt;// more explicit&lt;/span&gt;
            Session.Update(Model.Action);

            tx.Commit();
        }
        successfulSave = &lt;span class="kwrd"&gt;true&lt;/span&gt;;
    }
    &lt;span class="kwrd"&gt;catch&lt;/span&gt; (StaleObjectStateException)
    {
        var mergeResult = Presenters.ShowDialog&amp;lt;MergeResult?&amp;gt;(&lt;span class="str"&gt;"Merge"&lt;/span&gt;, Model.Action);
        successfulSave = mergeResult != &lt;span class="kwrd"&gt;null&lt;/span&gt;;

        ReplaceSessionAfterError();
    }

    &lt;span class="rem"&gt;// we call ActionUpdated anyway, either we updated the value ourselves&lt;/span&gt;
    &lt;span class="rem"&gt;// or we encountered a concurrency conflict, in which case we _still_&lt;/span&gt;
    &lt;span class="rem"&gt;// want other parts of the application to update themselves with the values&lt;/span&gt;
    &lt;span class="rem"&gt;// from the db&lt;/span&gt;
    EventPublisher.Publish(&lt;span class="kwrd"&gt;new&lt;/span&gt; ActionUpdated
    {
        Id = Model.Action.Id
    }, &lt;span class="kwrd"&gt;this&lt;/span&gt;);

    &lt;span class="kwrd"&gt;if&lt;/span&gt; (successfulSave)
        View.Close();
}&lt;/pre&gt;
  &lt;style type="text/css"&gt;&lt;![CDATA[
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;&lt;/blockquote&gt;

&lt;p&gt;Because:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;ReplaceSessionAfterError, to much responsibility for a presenter. &lt;/li&gt;

  &lt;li&gt;Session/Transaction again. &lt;/li&gt;

  &lt;li&gt;There is a BUG, the publish mechanism work in sync with the rest of the code. This means… that this windows is not going to close until others have finished handling the event. For the given example, the Edit windows is not going to close until the main window finish to refresh the current page. &lt;/li&gt;

  &lt;li&gt;Too much logic for this method = hard to test.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I fully agree that this is a complex method, and Jose’s refactoring for that is an improvement indeed.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;pre class="csharpcode"&gt;[PersistenceConversation(ConversationEndMode = EndMode.End)]&lt;br /&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;virtual&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; OnSave()
{
    _toDoActionsDao.Update(Model.Action);

    EventPublisher.Enlist(&lt;span class="kwrd"&gt;new&lt;/span&gt; ActionUpdated
    {
        Id = Model.Action.Id
    }, &lt;span class="kwrd"&gt;this&lt;/span&gt;);

    View.Close();
}

&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;override&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; OnException(Exception exception)
{
    &lt;span class="kwrd"&gt;if&lt;/span&gt;(exception &lt;span class="kwrd"&gt;is&lt;/span&gt; StaleEntityException)
    {
        Presenters.ShowDialog&amp;lt;MergeResult?&amp;gt;(&lt;span class="str"&gt;"Merge"&lt;/span&gt;, Model.Action);

        EventPublisher.Enlist(&lt;span class="kwrd"&gt;new&lt;/span&gt; ActionUpdated
        {
            Id = Model.Action.Id
        }, &lt;span class="kwrd"&gt;this&lt;/span&gt;);

    }
}&lt;/pre&gt;
  &lt;style type="text/css"&gt;&lt;![CDATA[
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;&lt;/blockquote&gt;

&lt;p&gt;Jose has implemented the following changes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;I’ve defined a new convention, if a public method in the presenter throw an exception, the OnException method will be called. This is done by a castle interceptor, and this is the last line of defense for unhandled exceptions. &lt;/li&gt;

  &lt;li&gt;I’m using “StaleEntityException” rather than “StaleObjectStateException”, this is MY exception. This is easily done by a CpBT artifact. &lt;/li&gt;

  &lt;li&gt;I’m not calling “EventPublisher.Publish” anymore, this code use EventPublisher.Enlist. Here, I’ve split the “Publish” code in two different methods one for Enlist and other for Raise. The enlisted events will be raised right after the OnSave method is called and thus after the windows is closed. &lt;/li&gt;

  &lt;li&gt;Also, notice that here is the conversation per business transaction pattern with all its splendor. The two former methods are conversation participants, with EndMode equals to continue. This means that the NH Session will remain opened. The OnSave method has EndMode equals to End, this means that right after the method finished, CpBT internally will flush the Unit of Work and close it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is better than the original implementation, but I think it can be made better still. First, OnException as a generic method is a bad idea. For the simple reason that the exception logic for different method is different, I would probably define a [MethodName]Error(Exception e) convention instead, which would make it easier to separate the error logic for different methods.&lt;/p&gt;

&lt;p&gt;Again, I don’t find any usefulness in abstracting the underlying framework, I haven’t seen a single case where it was useful, but I have seen a lot of cases where it was hurting the team &amp;amp; the project.&lt;/p&gt;

&lt;p&gt;The idea about splitting the publication and raising is really nice, I agree.&lt;/p&gt;

&lt;p&gt;However, there is a problem in the code with regards to session handling in cases of error. There is a pretty good reason why I introduced the ReplaceSessionAfterError method. In general, I want to keep my session alive for the duration of the form, because I get a lot of benefits out of that. &lt;em&gt;But&lt;/em&gt;, if the session has run into an error, I need to replace it &lt;em&gt;and all the objects associated with it&lt;/em&gt;. Closing the session no matter what is going on is not a good solution, and you can’t really solve the problem in a generic way without calling back to the presenter that generated the error.&lt;/p&gt;&lt;img src="http://ayende.com/Blog/aggbug/11271.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Ayende Rahien</dc:creator>
            <guid>http://ayende.com/Blog/archive/2010/01/08/responding-to-effectus-commentary.aspx</guid>
            <pubDate>Fri, 08 Jan 2010 10:00:00 GMT</pubDate>
            <comments>http://ayende.com/Blog/archive/2010/01/08/responding-to-effectus-commentary.aspx#feedback</comments>
            <slash:comments>10</slash:comments>
            <wfw:commentRss>http://ayende.com/Blog/comments/commentRss/11271.aspx</wfw:commentRss>
        </item>
        <item>
            <title>Setting the record straight: I am not the main contributor to NHibernate</title>
            <link>http://ayende.com/Blog/archive/2009/12/12/setting-the-record-straight-i-am-not-the-main-contributor.aspx</link>
            <description>&lt;p&gt;This is just something that bothers me. I’ll take credit where credit is due, but in this case, it isn’t. I am neither the owner of NHibernate nor the main contributor, as some people seems to think. &lt;/p&gt;  &lt;p&gt;According to &lt;a href="http://www.ohloh.net/p/nhibernate/contributors"&gt;ohloh&lt;/a&gt;, I am actually the #4 contributor, and if we will look at results from the last year alone, I would say that I am more likely to be #5 or #6. NHibernate is the work of many people, and it bothers me that people seems to think that because I talk about NHibernate frequently and for so long, I am actually the main guy behind it.&lt;/p&gt;  &lt;p&gt;I am not the main contributor, I am just the loudest one.&lt;/p&gt;&lt;img src="http://ayende.com/Blog/aggbug/11238.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Ayende Rahien</dc:creator>
            <guid>http://ayende.com/Blog/archive/2009/12/12/setting-the-record-straight-i-am-not-the-main-contributor.aspx</guid>
            <pubDate>Sat, 12 Dec 2009 10:00:00 GMT</pubDate>
            <comments>http://ayende.com/Blog/archive/2009/12/12/setting-the-record-straight-i-am-not-the-main-contributor.aspx#feedback</comments>
            <slash:comments>11</slash:comments>
            <wfw:commentRss>http://ayende.com/Blog/comments/commentRss/11238.aspx</wfw:commentRss>
        </item>
        <item>
            <title>Building a recommendation engine in NHibernate</title>
            <link>http://ayende.com/Blog/archive/2009/12/07/building-a-recommendation-engine-in-nhibernate.aspx</link>
            <description>&lt;p&gt;Well, it isn’t really a recommendation engine, it is a sample of one, and I strongly recommend &lt;em&gt;not&lt;/em&gt; using it, but I am getting ahead of myself.&lt;/p&gt;  &lt;p&gt;In the &lt;a href="http://tekpub.com/preview/nhibernate"&gt;6th episode of the TekPub’s NHibernate webcast&lt;/a&gt;, me &amp;amp; Rob worked on creating statistical queries with NHibernate. To be totally honest, the reason that we did that is to show off NHibernate’s querying capabilities, not so you would be able to make use of this in your applications. A recommendation engine is not something that you should run out of your OLTP store, so please take that under advisement.&lt;/p&gt;  &lt;p&gt;The reason for this post is to explain in details how the final result works. Here is the NHibernate code:&lt;/p&gt;  &lt;pre class="csharpcode"&gt;var orderIDsContainingCurrentSku = DetachedCriteria.For&amp;lt;OrderItem&amp;gt;()
            .Add&amp;lt;OrderItem&amp;gt;(x=&amp;gt;x.Product.SKU==sku)
            .SetProjection(Projections.Property(&lt;span class="str"&gt;"Order.id"&lt;/span&gt;));

var skusOfProductsAppearingInOrdersContainingCurrentSku = DetachedCriteria.For&amp;lt;OrderItem&amp;gt;()
    .SetProjection(Projections.GroupProperty(&lt;span class="str"&gt;"Product.id"&lt;/span&gt;))
    .AddOrder(NHibernate.Criterion.Order.Desc(Projections.Count(&lt;span class="str"&gt;"Order.id"&lt;/span&gt;)))
    .Add&amp;lt;OrderItem&amp;gt;(x=&amp;gt;x.Product.SKU!=sku)
    .Add(Subqueries.PropertyIn(&lt;span class="str"&gt;"Order.id"&lt;/span&gt;, orderIDsContainingCurrentSku))
    .SetMaxResults(15);


var recommended = _session.CreateCriteria&amp;lt;Product&amp;gt;()
    .SetFetchMode&amp;lt;Product&amp;gt;(x =&amp;gt; x.Descriptors, FetchMode.Join)
    .Add(Subqueries.PropertyIn(&lt;span class="str"&gt;"id"&lt;/span&gt;, skusOfProductsAppearingInOrdersContainingCurrentSku))
    .SetResultTransformer(Transformers.DistinctRootEntity)
    .List&amp;lt;Product&amp;gt;();&lt;/pre&gt;
&lt;style type="text/css"&gt;&lt;![CDATA[
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;

&lt;p&gt;And here is the resulting SQL:&lt;/p&gt;

&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; this_.SKU                 &lt;span class="kwrd"&gt;as&lt;/span&gt; SKU1_1_,
       this_.ProductName         &lt;span class="kwrd"&gt;as&lt;/span&gt; ProductN2_1_1_,
       this_.BasePrice           &lt;span class="kwrd"&gt;as&lt;/span&gt; BasePrice1_1_,
       this_.WeightInPounds      &lt;span class="kwrd"&gt;as&lt;/span&gt; WeightIn4_1_1_,
       this_.DateAvailable       &lt;span class="kwrd"&gt;as&lt;/span&gt; DateAvai5_1_1_,
       this_.EstimatedDelivery   &lt;span class="kwrd"&gt;as&lt;/span&gt; Estimate6_1_1_,
       this_.AllowBackOrder      &lt;span class="kwrd"&gt;as&lt;/span&gt; AllowBac7_1_1_,
       this_.IsTaxable           &lt;span class="kwrd"&gt;as&lt;/span&gt; IsTaxable1_1_,
       this_.DefaultImageFile    &lt;span class="kwrd"&gt;as&lt;/span&gt; DefaultI9_1_1_,
       this_.AmountOnHand        &lt;span class="kwrd"&gt;as&lt;/span&gt; AmountO10_1_1_,
       this_.AllowPreOrder       &lt;span class="kwrd"&gt;as&lt;/span&gt; AllowPr11_1_1_,
       this_.DeliveryMethodID    &lt;span class="kwrd"&gt;as&lt;/span&gt; Deliver12_1_1_,
       this_.InventoryStatusID   &lt;span class="kwrd"&gt;as&lt;/span&gt; Invento13_1_1_,
       descriptor2_.SKU          &lt;span class="kwrd"&gt;as&lt;/span&gt; SKU3_,
       descriptor2_.DescriptorID &lt;span class="kwrd"&gt;as&lt;/span&gt; Descript1_3_,
       descriptor2_.DescriptorID &lt;span class="kwrd"&gt;as&lt;/span&gt; Descript1_4_0_,
       descriptor2_.Title        &lt;span class="kwrd"&gt;as&lt;/span&gt; Title4_0_,
       descriptor2_.Body         &lt;span class="kwrd"&gt;as&lt;/span&gt; Body4_0_
&lt;span class="kwrd"&gt;FROM&lt;/span&gt;   Products this_
       &lt;span class="kwrd"&gt;left&lt;/span&gt; &lt;span class="kwrd"&gt;outer&lt;/span&gt; &lt;span class="kwrd"&gt;join&lt;/span&gt; ProductDescriptors descriptor2_
         &lt;span class="kwrd"&gt;on&lt;/span&gt; this_.SKU = descriptor2_.SKU
&lt;span class="kwrd"&gt;WHERE&lt;/span&gt;  this_.SKU &lt;span class="kwrd"&gt;in&lt;/span&gt; (&lt;span class="kwrd"&gt;SELECT&lt;/span&gt;   &lt;span class="kwrd"&gt;top&lt;/span&gt; 15 this_0_.SKU &lt;span class="kwrd"&gt;as&lt;/span&gt; y0_
                     &lt;span class="kwrd"&gt;FROM&lt;/span&gt;     OrderItems this_0_
                     &lt;span class="kwrd"&gt;WHERE&lt;/span&gt;    &lt;span class="kwrd"&gt;not&lt;/span&gt; (this_0_.SKU = &lt;span class="str"&gt;'Binoculars2'&lt;/span&gt; /* @p0 */)
                              &lt;span class="kwrd"&gt;and&lt;/span&gt; this_0_.OrderID &lt;span class="kwrd"&gt;in&lt;/span&gt; (&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; this_0_0_.OrderID &lt;span class="kwrd"&gt;as&lt;/span&gt; y0_
                                                      &lt;span class="kwrd"&gt;FROM&lt;/span&gt;   OrderItems this_0_0_
                                                      &lt;span class="kwrd"&gt;WHERE&lt;/span&gt;  this_0_0_.SKU = &lt;span class="str"&gt;'Binoculars2'&lt;/span&gt; /* @p1 */)
                     &lt;span class="kwrd"&gt;GROUP&lt;/span&gt; &lt;span class="kwrd"&gt;BY&lt;/span&gt; this_0_.SKU
                     &lt;span class="kwrd"&gt;ORDER&lt;/span&gt; &lt;span class="kwrd"&gt;BY&lt;/span&gt; &lt;span class="kwrd"&gt;count&lt;/span&gt;(this_0_.OrderID) &lt;span class="kwrd"&gt;desc&lt;/span&gt;)&lt;/pre&gt;

&lt;pre class="csharpcode"&gt; &lt;/pre&gt;

&lt;p&gt;The problem is that both the NHibernate code and the SQL are pretty complicated, and mapping between the two might be pretty hard if you are not familiar with that. So let us take this in stages. First, let us understand the logic in the SQL itself. Most of the complexity happens in the where clause, so let us look at this in depth:&lt;/p&gt;

&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;WHERE&lt;/span&gt;  this_.SKU &lt;span class="kwrd"&gt;in&lt;/span&gt; (&lt;span class="kwrd"&gt;SELECT&lt;/span&gt;   &lt;span class="kwrd"&gt;top&lt;/span&gt; 15 this_0_.SKU &lt;span class="kwrd"&gt;as&lt;/span&gt; y0_
 &lt;span class="kwrd"&gt;FROM&lt;/span&gt;     OrderItems this_0_
 &lt;span class="kwrd"&gt;WHERE&lt;/span&gt;    &lt;span class="kwrd"&gt;not&lt;/span&gt; (this_0_.SKU = &lt;span class="str"&gt;'Binoculars2'&lt;/span&gt; /* @p0 */)
          &lt;span class="kwrd"&gt;and&lt;/span&gt; this_0_.OrderID &lt;span class="kwrd"&gt;in&lt;/span&gt; (&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; this_0_0_.OrderID &lt;span class="kwrd"&gt;as&lt;/span&gt; y0_
                  &lt;span class="kwrd"&gt;FROM&lt;/span&gt;   OrderItems this_0_0_
                  &lt;span class="kwrd"&gt;WHERE&lt;/span&gt;  this_0_0_.SKU = &lt;span class="str"&gt;'Binoculars2'&lt;/span&gt; /* @p1 */)
 &lt;span class="kwrd"&gt;GROUP&lt;/span&gt; &lt;span class="kwrd"&gt;BY&lt;/span&gt; this_0_.SKU
 &lt;span class="kwrd"&gt;ORDER&lt;/span&gt; &lt;span class="kwrd"&gt;BY&lt;/span&gt; &lt;span class="kwrd"&gt;count&lt;/span&gt;(this_0_.OrderID) &lt;span class="kwrd"&gt;desc&lt;/span&gt;)&lt;/pre&gt;
&lt;style type="text/css"&gt;&lt;![CDATA[
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;

&lt;p&gt;What exactly is going on in here?&lt;/p&gt;

&lt;p&gt;Let us look at the nested most select, this select the OrderId from an order that have a Binoculars item in it. That is then passed to the parent select, matching it to that OrderID and returning the SKU if it is not also a Binoculars.&lt;/p&gt;

&lt;p&gt;What we are actually saying here is: Give me all the items from orders that contains Binoculars, except for Binoculars. The logic is simple, you are very likely to buy something that someone else also bought in an order together with stuff that you bought (complimentary products, another book in the same series, etc). Next, we have the order by, we use that to find the stuff that you are most likely to buy. By ordering the items based on the number of orders they appear in, we try to find the most popular items (hence, the stuff that &lt;em&gt;you&lt;/em&gt; are likely to buy as well).&lt;/p&gt;

&lt;p&gt;I think that this is fairly clear, and now that we have the logic of the statement, let us try to understand how that NHibernate code produced it. &lt;/p&gt;

&lt;p&gt;The answer is actually very simple, NHibernate’s Criteria API is all about composability. We simply composed the query from all the tiny pieces. Let us look at each individual piece in detail:&lt;/p&gt;

&lt;pre class="csharpcode"&gt;var orderIDsContainingCurrentSku = DetachedCriteria.For&amp;lt;OrderItem&amp;gt;()
            .Add&amp;lt;OrderItem&amp;gt;(x=&amp;gt;x.Product.SKU==sku)
            .SetProjection(Projections.Property(&lt;span class="str"&gt;"Order.id"&lt;/span&gt;));&lt;/pre&gt;
&lt;style type="text/css"&gt;&lt;![CDATA[
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;

&lt;p&gt;This query is using DetachedCriteria, this is a way to generate a query (or a sub query, as we will soon see), without having a direct reference to the session. This is mostly useful in cases like this, where you want to compose several queries into a single one. &lt;/p&gt;

&lt;p&gt;&lt;a href="http://ayende.com/Blog/images/ayende_com/Blog/WindowsLiveWriter/BuildingarecommendationengineinNHibernat_C750/image_2.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; margin-left: 0px; border-top: 0px; margin-right: 0px; border-right: 0px" title="image" border="0" alt="image" align="right" src="http://ayende.com/Blog/images/ayende_com/Blog/WindowsLiveWriter/BuildingarecommendationengineinNHibernat_C750/image_thumb.png" width="440" height="183" /&gt;&lt;/a&gt; In this case, it is pretty obvious what is going on, we ask NHibernate to select from OrderItems (by using the OrderItem entity), where the product sku is equal to the appropriate SKU (Binoculars2, in this case), and we don’t want to get the entity back, instead, we want only a single field (this is what SetProjection is for), the order id. Note that OrderItem mapping is quite interesting:&lt;/p&gt;

&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;class&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;="OrderItem"&lt;/span&gt; &lt;span class="attr"&gt;table&lt;/span&gt;&lt;span class="kwrd"&gt;="OrderItems"&lt;/span&gt; &lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;composite-id&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;key-many-to-one&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;="Product"&lt;/span&gt; &lt;span class="attr"&gt;column&lt;/span&gt;&lt;span class="kwrd"&gt;="SKU"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;key-many-to-one&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;="Order"&lt;/span&gt; &lt;span class="attr"&gt;column&lt;/span&gt;&lt;span class="kwrd"&gt;="OrderID"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;composite-id&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;class&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;style type="text/css"&gt;&lt;![CDATA[
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;

&lt;p&gt;It is a class with a composite id, where each part of the PK is also a FK to a different table. With NHibernate, we map this using &amp;lt;key-many-to-one/&amp;gt; in a composite-id element.&lt;/p&gt;

&lt;p&gt;When we want to query on that, we can either use the usual many-to-one approach, or, if we want to refer to a particular column, we use the “id” (all lower case) keyword. In other words, “Order.id” refers to the OrderItems.OrderID column, while “Product.id” or “Product.SKU” refers to the “OrderItems.SKU” column. I think that you can figure out what is going on now, this query generate the following SQL:&lt;/p&gt;

&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; this_0_0_.OrderID &lt;span class="kwrd"&gt;as&lt;/span&gt; y0_
&lt;span class="kwrd"&gt;FROM&lt;/span&gt;   OrderItems this_0_0_
&lt;span class="kwrd"&gt;WHERE&lt;/span&gt;  this_0_0_.SKU = &lt;span class="str"&gt;'Binoculars2'&lt;/span&gt; /* @p1 */&lt;/pre&gt;
&lt;style type="text/css"&gt;&lt;![CDATA[
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;

&lt;p&gt;And I think that can see the direct correlation between the NHibernate query and the generated SQL.&lt;/p&gt;

&lt;p&gt;Next in line, and seemingly much more complicated, we have this:&lt;/p&gt;

&lt;pre class="csharpcode"&gt;var skusOfProductsAppearingInOrdersContainingCurrentSku = DetachedCriteria.For&amp;lt;OrderItem&amp;gt;()
    .SetProjection(Projections.GroupProperty(&lt;span class="str"&gt;"Product.id"&lt;/span&gt;))
    .AddOrder(NHibernate.Criterion.Order.Desc(Projections.Count(&lt;span class="str"&gt;"Order.id"&lt;/span&gt;)))
    .Add&amp;lt;OrderItem&amp;gt;(x=&amp;gt;x.Product.SKU!=sku)
    .Add(Subqueries.PropertyIn(&lt;span class="str"&gt;"Order.id"&lt;/span&gt;, orderIDsContainingCurrentSku))
    .SetMaxResults(15);&lt;/pre&gt;
&lt;style type="text/css"&gt;&lt;![CDATA[
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;

&lt;p&gt;But it isn’t really complidated, let us look at this in details. The first line is already familiar for us, asking to select from OrderItems. Next, we use SetProjection again, to select just the “Product.id”, which is the OrderItems.SKU. Note that we are using something slightly different this time, where before we used Projections.Property, now we use Projections.GroupProperty. What is the difference between the two?&lt;/p&gt;

&lt;p&gt;Projection.Property instructs NHibernate to put the matching column in the select clause, while Projection.GroupProperty instructs NHibernate to put the matching column in the select clause and in the group by clause. This is required because on the &lt;em&gt;next&lt;/em&gt; line, we are using an aggregate function in the order by clause, aggregate functions must be used in conjunction with the appropriate group by clause. That line also specify that we are using a descending order on the count of the “Order.id” (which matches OrderItems.OrderID).&lt;/p&gt;

&lt;p&gt;The following line is something we are already familiar with, we are adding a where clause to filter the current SKU. And now we get to the interesting part, we use a subquery to match the “Order.id” to the order ids containing the current SKU. Last, but not least, we limit the number of returned rows to 15. The resulting SQL is:&lt;/p&gt;

&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;SELECT&lt;/span&gt;   &lt;span class="kwrd"&gt;top&lt;/span&gt; 15 this_0_.SKU &lt;span class="kwrd"&gt;as&lt;/span&gt; y0_
&lt;span class="kwrd"&gt;FROM&lt;/span&gt;     OrderItems this_0_
&lt;span class="kwrd"&gt;WHERE&lt;/span&gt;    &lt;span class="kwrd"&gt;not&lt;/span&gt; (this_0_.SKU = &lt;span class="str"&gt;'Binoculars2'&lt;/span&gt; /* @p0 */)
      &lt;span class="kwrd"&gt;and&lt;/span&gt; this_0_.OrderID &lt;span class="kwrd"&gt;in&lt;/span&gt; (/* orderIDsContainingCurrentSku */)
&lt;span class="kwrd"&gt;GROUP&lt;/span&gt; &lt;span class="kwrd"&gt;BY&lt;/span&gt; this_0_.SKU
&lt;span class="kwrd"&gt;ORDER&lt;/span&gt; &lt;span class="kwrd"&gt;BY&lt;/span&gt; &lt;span class="kwrd"&gt;count&lt;/span&gt;(this_0_.OrderID) desc&lt;/pre&gt;
&lt;style type="text/css"&gt;&lt;![CDATA[
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;

&lt;p&gt;I think that again, once we have gone over this in details, you will agree that there is a pretty simple mapping between the query and the resulting SQL.&lt;/p&gt;

&lt;p&gt;Now, let us look at the actual query code, which make use of the previous two subqueries:&lt;/p&gt;

&lt;pre class="csharpcode"&gt;var recommended = _session.CreateCriteria&amp;lt;Product&amp;gt;()
    .SetFetchMode&amp;lt;Product&amp;gt;(x =&amp;gt; x.Descriptors, FetchMode.Join)
    .Add(Subqueries.PropertyIn(&lt;span class="str"&gt;"id"&lt;/span&gt;, skusOfProductsAppearingInOrdersContainingCurrentSku))
    .SetResultTransformer(Transformers.DistinctRootEntity)
    .List&amp;lt;Product&amp;gt;();&lt;/pre&gt;
&lt;style type="text/css"&gt;&lt;![CDATA[
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]&gt;&lt;/style&gt;

&lt;p&gt;We create a standard criteria query, ask it to eager load the Descriptors, then we perform a subquery, matching the product “id” (when specified using all lower case it is an NHibernate keyword referencing the current entity’s PK column) to the skus of products that appear in orders contains the current sku. Because we used a join to eager load the descriptors, we need to specify SetResultTransofer so we will get only distinct root entities.&lt;/p&gt;

&lt;p&gt;All in all, when you break it up to pieces like that, I don’t think that it is an overly complex process. The query we were trying to get to is by no means a simple one, but we didn’t have any additional complexity when trying to create it using NHibernate.&lt;/p&gt;&lt;img src="http://ayende.com/Blog/aggbug/11237.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Ayende Rahien</dc:creator>
            <guid>http://ayende.com/Blog/archive/2009/12/07/building-a-recommendation-engine-in-nhibernate.aspx</guid>
            <pubDate>Mon, 07 Dec 2009 12:20:00 GMT</pubDate>
            <comments>http://ayende.com/Blog/archive/2009/12/07/building-a-recommendation-engine-in-nhibernate.aspx#feedback</comments>
            <slash:comments>18</slash:comments>
            <wfw:commentRss>http://ayende.com/Blog/comments/commentRss/11237.aspx</wfw:commentRss>
        </item>
    </channel>
</rss>