Effectively retrieving object with nested collections using NHibernate - nhibernate

I am creating a survey application where I have a survey which has a collection of pages. Each page will have a collection of questions and each question will have a collection of answer options. My class structure looks like:
public class Survey : Entity {
public IList<Page> Pages { get; set; }
}
public class Page : Entity {
public IList<Question> Questions { get;set; }
}
public class Question : Entity {
public IList<Option> Options { get; set; }
}
public class Option : Entity {}
The mapping for each class is:
<!-- mapping for ID and other properties excluded -->
<class name="Survey">
<bag name="Pages" generic="true" inverse="true">
<key column="SurveyId" />
<one-to-many class="Page" />
</bag>
<bag name="Questions" access="none">
<key column="SurveyId" />
<one-to-many class="Question" />
</bag>
</class>
<class name="Page">
<many-to-one name="Survey" column="SurveyId" />
<bag name="Questions" generic="true" inverse="true">
<key column="PageId" />
<one-to-many class="Question" />
</bag>
</class>
<class name="Question">
<many-to-one name="Page" column="PageId" />
<many-to-one name="Survey" column="SurveyId" />
<bag name="Options" generic="true" inverse="true">
<key column="QuestionId" />
<one-to-many class="Option" />
</bag>
</class>
<class name="AnswerOption">
<many-to-one name="Question" column="QuestionId" />
</class>
I need to display all the questions on a page so I start with the survey object and loop through the pages, items and options. This causes NHibernate to execute many queries and I would like to optimize this. How can I get the survey object with the nested collections in the best possible way without executing too many queries?
This is the code I have at the moment but it still executes many queries:
var result = Session.CreateMultiQuery()
.Add(Session.CreateQuery("from Survey s inner join fetch s.Pages where s.Id = :id"))
.Add(Session.CreateQuery("from Survey s inner join fetch s.Question where s.Id = :id"))
.SetInt32("id", id)
.List();
IList list = (IList)result[0];
return list[0] as Survey;
I have also tried Future queries but they don't help to reduce the number of queries.
Any ideas?

If I understood correctly, you can achieve this with the following HQL (of course the same idea can be used with ICriteria)
from Survey s
inner join fetch s.Pages p
inner join fetch p.Questions q
inner join fetch q.Options
where s.Id = :id
If you also want to fetch survey.Questions then the best option is to fetch those in separate qyery and used Futures to avoid cartesian product.
Also, if I remember correctly HQL queries ignores fetch's defined in mappings. If the collection is mapped with lazy="false" fetch="join" then it is fetch when used ICriteria but not when using HQL.

You could try adding
lazy="false" fetch="join"
to your bag declarations. That way you can be sure the bag will be fetched using one query.

A solution I'm using is reversing relation between entities. With this you can be sure when a record of Survey is loaded, no record of Page is loaded unless you call LoadAll method. LoadAll is a method in each class that search all related records. Consider following code:
public class Survey : Entity {
Survey[] LoadAll {
string hql = "from Page page where page.SurveyID =" + this.ID;
//....
}
}
public class Page : Entity {
public Survey { get;set; }
}
public class Question : Entity {
public Page Page { get; set; }
}
public class Option : Entity {
public Question Question {set; get;}
}

Related

NHibernate eager loaded tree hits the database when walked by Automapper

I ran into a strange NHibernate and Automapper problem. I am not sure which one is to blame but I am struggling for a whole day now and I can't seem to find out why.
Here is my Nhibernate mapping files:
Navigation.hbm.xml
<id name="ID" column="NavigationID">
<generator class="identity"></generator>
</id>
<property name="IsDefault"/>
<property name="RoleType" column="RoleTypeID" />
<bag name="Items" cascade="save-update" inverse="true" lazy="false" fetch="join">
<key column="NavigationID"/>
<one-to-many class="NavigationItem"/>
</bag>
NavigationItem.hbm.xml
<class name="NavigationItem" table="NavigationItem">
<id name="ID" column="NavigationItemID">
<generator class="identity"></generator>
</id>
<property name="ShowInMenu"/>
<property name="Order" column="[Order]" />
<many-to-one name="Page" column="PageID" lazy="false" fetch="join" />
<many-to-one name="Navigation" column="NavigationID" />
<many-to-one name="Parent" column="ParentNavigationItemID" />
<bag name="Items" cascade="save-update" inverse="true">
<key column="ParentNavigationItemID"/>
<one-to-many class="NavigationItem"/>
</bag>
This is how I fill up a Navigation object:
ISession session = SessionProvider.Instance.CurrentSession;
using (transaction = session.BeginTransaction())
{
var navigation = session.QueryOver<Navigation>()
.Where(x => x.IsDefault && x.RoleType == null)
.TransformUsing(new NHibernate.Transform.RootEntityResultTransformer())
.SingleOrDefault();
transaction.Commit();
return navigation;
}
Since the Items bag on the Navigation object is set to lazy="false", I get only one query to the database to get the Navigation object and a left join to get all the Navigation items as well.
All is perfect until now.
I did a test to iterate through all the items and the sub-items recursive and no more hits to the database.
Then, I have an UI model that I map with Automapper.
Here are the UI models:
public class NavigationModel
{
public List<NavigationItemModel> Items { get; set; }
public NavigationModel()
{
Items = new List<NavigationItemModel>();
}
}
public class NavigationItemModel
{
public string PageName { get; set; }
public string Url { get; set; }
public bool Selected { get; set; }
public NavigationItemModel Parent { get; set; }
public List<NavigationItemModel> Items { get; set; }
}
And the automapper mappings:
AutoMapper.Mapper
.CreateMap<NavigationItem, NavigationItemModel>()
// IF I REMOVE THE NEXT LINE, IT HITS THE DATABASE FOR EACH SUB-ITEM of the NavigationItem.Items
.ForMember(m => m.Items, o => o.Ignore());
AutoMapper.Mapper
.CreateMap<Navigation, NavigationModel>();
Ok, now the behavior is like this:
If I ignore the NavigationItem.Items member in the mapping, all goes well, but only the Navigation and it's items are mapped. No sub-items collection of the navigation's Items are mapped. BUT the database is not hit anymore. But I want the other items mapped as well...
If I remove the line under the comment, the database is hit for each of the Navigation.Items, querying for it's sub-items (where ParentID = Item.ID).
Any idea what am I doing wrong?
Sorry for the wall of text, but I thought better to describe it in more detail, I spent the whole day on this one and I tried all kind of queries with Future and JoinQueryOver, etc. The problem does not seem to be with NHibernate since that loads fine and I can iterate without any more calls to the database.
I forgot to include the SQL that is being generated:
First there is this query:
SELECT this_.NavigationID as Navigati1_7_2_,
this_.IsDefault as IsDefault7_2_,
this_.RoleTypeID as RoleTypeID7_2_,
items2_.NavigationID as Navigati5_4_,
items2_.NavigationItemID as Navigati1_4_,
items2_.NavigationItemID as Navigati1_4_0_,
items2_.ShowInMenu as ShowInMenu4_0_,
items2_.[Order] as column3_4_0_,
items2_.PageID as PageID4_0_,
items2_.NavigationID as Navigati5_4_0_,
items2_.ParentNavigationItemID as ParentNa6_4_0_,
page3_.PageID as PageID8_1_,
page3_.Name as Name8_1_,
page3_.Title as Title8_1_,
page3_.Description as Descript4_8_1_,
page3_.URL as URL8_1_
FROM Navigation this_
left outer join NavigationItem items2_
on this_.NavigationID = items2_.NavigationID
left outer join Page page3_
on items2_.PageID = page3_.PageID
WHERE (this_.IsDefault = 1 /* #p0 */
and this_.RoleTypeID is null)
Then, when Automapper comes into play, a list of these queries are being generated, only the p0 parameter differs (from 1 to 12 ... the number of items without parents )
SELECT items0_.ParentNavigationItemID as ParentNa6_2_,
items0_.NavigationItemID as Navigati1_2_,
items0_.NavigationItemID as Navigati1_4_1_,
items0_.ShowInMenu as ShowInMenu4_1_,
items0_.[Order] as column3_4_1_,
items0_.PageID as PageID4_1_,
items0_.NavigationID as Navigati5_4_1_,
items0_.ParentNavigationItemID as ParentNa6_4_1_,
page1_.PageID as PageID8_0_,
page1_.Name as Name8_0_,
page1_.Title as Title8_0_,
page1_.Description as Descript4_8_0_,
page1_.URL as URL8_0_
FROM NavigationItem items0_
left outer join Page page1_
on items0_.PageID = page1_.PageID
WHERE items0_.ParentNavigationItemID = 1 /* #p0 */
This is taken from the NHProf application, hope it helps.
Thank you,
Cosmin
I think AutoMapper is mapping your classes recursively. If this is the case, than you can specifiy the max depth for your mappings using
Mapper.CreateMap<TSource, TDestination>().MaxDepth(2); // or 1, or 3, or whatever

NHibernate Mapping - many-to-one via table

I'm working with an existing database that has the following structure. Changing the database schema is a last resort.
Products
Id
Name
ParentProducts
ParentId
ChildId
I don't want an entity for ParentProducts, I have the following for the children property (still need to test it, but that's the concept).
<bag name="Children" lazy="true" table="dbo.ParentProducts" cascade="save-update" inverse="true" >
<key column="[ChildId]"></key>
<many-to-many column="[ProductId]" class="Product" />
</bag>
What I'm struggling with is how do I create a Parent property? I'd like to do something like the following, but table isn't a valid attribute for many-to-one.
<many-to-one name="Parent" column="[ParentId]" table="dbo.ParentRelated" class="Policy" />
I could create a bag and only ever look at the first item, but that's more of a hack.
Any ideas?
Thanks
Creating a bag is the easiest solution. And you can give it a clean interface:
protected virtual ICollection<Product> Parents { get; set; }
public virtual Product Parent
{
get
{
return Parents.SingleOrDefault();
}
set
{
Parents.Clear();
Parents.Add(value);
}
}
With this, the rest of the code doesn't need to be aware of the DB/mapping structure.

How to map this class in NHibernate (not FluentNHibernate)?

Suppose I have a database like this:
This is set up to give role-wise menu permissions.
Please note that, User-table has no direct relationship with Permission-table.
Then how should I map this class against the database-tables?
class User
{
public int ID { get; set; }
public string Name { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public bool? IsActive { get; set; }
public IList<Role> RoleItems { get; set; }
public IList<Permission> PermissionItems { get; set; }
public IList<string> MenuItemKeys { get; set; }
}
This means,
(1) Every user has some Roles.
(2) Every user has some Permissions (depending on to Roles).
(3) Every user has some permitted MenuItemKeys (according to Permissions).
How should my User.hbm.xml look like?
Roles and Permissions are likely to be accessed a lot in the application. They are very likely to be in the second level cache, which means we can expect to efficiently iterate the User.RoleItems and Role.Permissions.
This has the advantage that we can generally expect to perform no queries when iterating those collections.
You could map the classes as follows.
The properties User.PermissionItems and User.MenuItemKeys are derived from the persistent entities, and thus do not appear in the mappings.
<class name="User" table="user">
<id name="ID">
<generator class="native"/>
</id>
<property name="Name"/>
<property name="Username"/>
<property name="Password"/>
<property name="IsActive"/>
<bag name="RoleItems" table="userrole" lazy="true">
<key column="userid" />
<many-to-many class="Role" column="roleid"/>
</bag>
</class>
<class name="Role" table="role">
<id name="ID">
<generator class="native"/>
</id>
<property name="RoleName"/>
<property name="IsActive"/>
<bag name="Permissions" table="permission">
<key column="roleid" />
<one-to-many class="Permission"/>
</bag>
</class>
<class name="Permission" table="permission">
<id name="ID">
<generator class="native"/>
</id>
<property name="MenuItemKey"/>
</class>
I would make the 2 additional lists you had on User into derived enumerations. If they were lists, there is no unambiguous way to insert into them since you cannot know to which role the value applies. Also, a Role is not owned by a User.
Update: now using Diego's improved version of these properties.
class User
{
public virtual IEnumerable<Permission> PermissionItems
{
get {
return RoleItems.SelectMany(role => role.PermissionItems);
}
}
public virtual IEnumerable<string> MenuItemKeys
{
get {
return RoleItems.SelectMany(role => role.PermissionItems,
(role, permission) => permission.MenuItemKey);
}
}
}
The mapping posted by Lachlan is the best alternative. You could use queries that perform all the joins for each collection, but that'd make them read only for practical purposes.
There is a much easier way to implement the property code, however, that might help you decide:
public IEnumerable<Permission> PermissionItems
{
get
{
return RoleItems.SelectMany(role => role.PermissionItems);
}
}
public IEnumerable<string> MenuItemKeys
{
get
{
return RoleItems.SelectMany(role => role.PermissionItems,
(role, permission) => permission.MenuItemKey);
}
}
Here's a link: Chapter 6. Collection mapping
Here's another useful link: Chapter 7. Association Mappings
EDIT
After having reasearched for an entire evening, I came to the following conclusion:
Considering NHibernate Best Practices, what you wish to do is no good;
Don't use exotic association mappings.
Good usecases for a real many-to-many associations are rare. Most of the time you need additional information stored in the "link table". In this case, it is much better to use two one-to-many associations to an intermediate link class. In fact, we think that most associations are one-to-many and many-to-one, you should be careful when using any other association style and ask yourself if it is really neccessary.
As a programming philosophy, I prefer to keep it simple than having to write clever code where even me would no more understand what I wrote after a certain time;
Plus, I even considered using the subquery element of association mapping which would have worked if I would have found a way to parameterize it, if it is doable, but it seems it won't let me parameterize the query with the User instance's Id property value;
In the optic of a well designed OO model, a child being aware of his parent's properties is fine, but a parent accessing a child's property makes no sens - design smell;
As I may understand considering the context exposed the benefits of having permissions or MenuItemKey values accessible from the User directly, I suggest the following solution:
Create yourself a user defined dataview which will hold the values related to the MenuItemKey Permission attribute gotten through the Roles the User is a member like so:
CREATE VIEW udvUsersPermissions AS
SELECT UR.UserID, P.ID as N'ID', P.MenuItemKey
FROM Users U
INNER JOIN UsersRoles UR ON UR.UserID = U.ID
INNER JOIN Roles R ON R.ID = UR.RoleID
INNER JOIN Permissions P ON P.RoleID = R.ID
GO
Then, map it according in you User.hbm.xml file:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="User" table="Users">
<id name="Id" column="ID">
<generator class="identity"/>
</id>
<property name="Name" length="100"/>
<property name="UserName" length="10" not-null="true"/>
<property name="Password" length="10" not-null="true"/>
<property name="IsActive" not-null="true"/>
<list name="Roles" table="UsersRoles" access="private-property" lazy="true">
<key column="UserID" foreign-key="FK_UR_U"/>
<list-index column="UserID"/>
<many-to-many class="Role" column="RoleID" />
</list>
<!-- Here mapping Permissions granted to User. -->
<list name="Permissions" table="udvUsersPermissions" lazy="true">
<key column="UserID"/>
<list-index column="MenuItemKey"/>
<many-to-many column="ID" class="Permission"/>
</list>
</class>
</hibernate-mapping>
And here, I will let you know about the subselect solution, in case it works the way I didn't expect it to.
<list name="Permissions" lazy="true">
<subselect> <!-- see section 7.6, Chapter 7 - Association mappings -->
select U.ID, P.ID, P.MenuItemKey
from Users U
inner join UsersRoles UR ON UR.UserID = U.ID
inner join Roles R ON R.ID = UR.RoleID
inner join Permissions P ON P.RoleID = R.ID
group by U.ID, P.ID, P.MenuItemKey
order by P.MenuItemKey
</subselect>
<key column="U.ID"/>
<list-index column="P.MenuItemKey"/>
<many-to-many class="Permission" column="P.ID"/>
</list>
Now, I hope I brought enough details so that it helps you achieve what you want to do or either get on track. =)

(Detached)Criteria equivalent for HQL's 'index' function

I have an IDictionary on an object which I'm loading with the following mapping:
public class InternalFund : IInternalFund
{
public virtual IDictionary<DateTime, IValuation> Valuations { get; set; }
}
<class name="InternalFund">
<map name="Valuations">
<key>
<column name="FundID" />
</key>
<index type="DateTime" column="ValuationDate" />
<one-to-many class="Echo.EchoDomain.Portfolio.Valuation" />
</map>
</class>
This works fine, the Valuation object doesn't have a ValuationDate on it but Nhibernate is loading the ValuationDate into the key of the dictionary as desired. I want to query the InternalFund retrieving just one Valuation specifying the ValuationDate. I've managed to do this using the index() function in HQL:
"from InternalFund i left join fetch i.Valuations v where index(v)='2009-09-30'"
Again this is fantastic and exactly what I want producing the following where clause:
((valuations1_.ValuationDate='2009-09-30' ))
But I'd really like to do this in a DetachedCriteria to preserve the sanity of my project. When I try
.Add(Restrictions.Eq("index(Valuations)", valuationDate));
Or
.CreateAlias("Valuations", "v", JoinType.LeftOuterJoin)
.Add(Restrictions.Eq("index(v)", valuationDate));
It says:
QueryException: could not resolve property: index(v) of: Echo.EchoDomain.Fund.InternalFund
Is there a way to run index() with a DetachedCriteria?
Thanks
Stu
I believe that it is not possible (yet?)
See this feature-request / improvement request on NHibernate JIRA.

NHibernate one-to-many relationship lazy loading when already loaded

I have a tree where every node is a Resource class:
public abstract class Resource
{
public virtual Guid Id { get; set; }
public virtual Resource Parent { get; set; }
public virtual IList<Resource> ChildResources { get; set; }
}
as you can see this class is abstract and there are many different derived classes from Resource (3 at the moment, more to come).
In my database i have a table for Resource, and a table for each class which
derives from Resource. These are mapped together with <joined-subclass>.
I've read this:
http://ayende.com/Blog/archive/2009/08/28/nhibernate-tips-amp-tricks-efficiently-selecting-a-tree.aspx
and i have the same code as Ayende to load my tree:
var resource = UnitOfWork.Current.Session
.CreateQuery("from Resource r join fetch r.ChildResources")
.SetResultTransformer(new DistinctRootEntityResultTransformer())
.SetReadOnly(true)
.List<Resource>();
which is all working fine (all Resources are returned with a single select) However, I'm seeing extra selects occurring as I enumerate a Resource's ChildResources list.
Is that because of this?:
http://ayende.com/Blog/archive/2009/09/03/answer-the-lazy-loaded-inheritance-many-to-one-association-orm.aspx
Either way, how do I prevent this from happening?
Here's the part of the mappings for the relationships (class names
trimmed for clarity):
<bag cascade="save-update" fetch="join" lazy="false" inverse="true" name="ChildResources">
<key>
<column name="Parent_Id" />
</key>
<one-to-many class="Resource" />
</bag>
<many-to-one class="Resource" name="Parent">
<column name="Parent_Id" />
</many-to-one>
Thanks
UPDATE
Slight oversight, its only issuing extra selects when enumerating the child collections of the leaf nodes in the tree...
Either do this:
<bag ... lazy="false">
to eager fetch the items always, or do this (in HQL):
var resources = session.CreateQuery("from Resource r join fetch r.ChildResources");