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
Related
i have old legacy DB which has dead links in their tables. I have class mapped in nhibernate like this:
<class name="Visible" table="table_visible">
<composite-id>
<key-many-to-one column="object_id" name="ObjectA" />
<key-many-to-one column="sub_object_id" name="SubObject" />
</composite-id>
<property column="visible" name="VisibleRow" />
</class>
and:
public class Visible
{
public virtual ObjectAClass ObjectA { get; set; }
public virtual SubObjectClass SubObject { get; set; }
public virtual bool VisibleRow { get; set; }
public override bool Equals(object obj)
{
var other = ((Visible)obj);
return this.ObjectA.Equals(other.ObjectA) && this.SubObject.Equals(other.SubObject);
}
public override int GetHashCode()
{
return this.ObjectA.GetHashCode() + (this.SubObject != null? this.SubObject.GetHashCode(): 0);
}
}
Now all works fine when all joins in database are correct, but when i find such sub_object_id which doesnt have entity, nhibernate throws me error
No row with the given identifier exists:[SubObject#123]
Is there a way to map composite key so that when its subentity is not found, the whole entity wouldnt be loaded (like with inner join)?
NHibernate v2.0.50727
Following Daniel Schilling idea of fetch Visible entities with a where exists sub-query, found that there is loader element available in mappings.
<class name="ObjectA" table="table_object">
.........
<set name="VisibleList" cascade="all" lazy="false" inverse="true">
<key column="object_id" />
<one-to-many class="Visible" />
<loader query-ref="valid_entities"/>
</set>
</class>
<sql-query name="valid_entities">
<load-collection alias="v" role="ObjectA.VisibleList"/>
SELECT {v.*}
FROM table_visible v
INNER JOIN table_sub_entities e ON e.sub_entity_id=v.sub_entity_id
WHERE v.object_id=?
</sql-query>
And nothing else needed to be changed.
<key-many-to-one column="sub_object_id" name="SubObject" not-found="ignore" />
... may be helpful. From the NHibernate Documentation...
ignore will treat a missing row as a null association
Please be aware of the performance penalty associated with using this option. Whenever NHibernate fetches a Visible entity, it will also have to fetch SubObject. If you don't go ahead and fetch it in your query, this means that NHibernate will be issuing lots of lazy loads.
This doesn't meet your "when its sub-entity is not found, the whole entity wouldn't be loaded" goal. Instead NHibernate would give you an entity with a null sub-entity. If you want that inner-join-like behavior, then I think you would need to fetch your Visible entities with a where exists sub-query to make sure the SubObject actually exists.
The best option would be to fix the data in the database and add a foreign key constraint.
I just ran across this: Relations with not-found="ignore". I promise I'm not copying Ricci's content - I'm writing this from my own experience.
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;}
}
I have the following entities:
namespace NhLists {
public class Lesson {
public virtual int Id { get; set; }
public virtual string Title { get; set; }
}
public class Module {
public virtual int Id { get; set; }
public virtual IList<Lesson> Lessons { get; set; }
public Module() {
Lessons = new List<Lesson>();
}
}
}
And the following mappings:
<class name="Module" table="Modules">
<id name="Id">
<generator class="identity"/>
</id>
<list name="Lessons" table="ModuleToLesson"
cascade="save-update">
<key column="moduleId"/>
<index column="position"/>
<many-to-many
column="lessonId"
class="NhLists.Lesson, NhLists"/>
</list>
</class>
<class name="Lesson" table="Lessons">
<id name="Id">
<generator class="identity"/>
</id>
<property name="Title">
<column name="Title" length="16" not-null="true" />
</property>
</class>
When I delete a lesson by session.Delete(lesson), is there anyway I can have NHibernate automatically update the association in Module.Lessons to remove the entry from the set? Or am I forced to go through all Modules and look for the lesson and remove that by hand?
Edit: Fixed ICollection and <set> in mappings to IList<> and <list> like I want and tested it.
You have false idea. If you want to delete the Lesson object from Module you do that manually. NHibernate just tracks such your action and when session.Commit() is called then the reference between Module and Lesson is deleted in the database.
Calling session.Delete(lesson) deletes the lesson object from database (if foreign keys are set properly then reference between Module and Lesson is deleted of course but it is not responsibility for NHibernate).
In conclusion, it is not possible to delete the lesson object from the Module.Lessons list automatically by calling session.Delete(lesson). NHibernate does not track such entity references.
Turns out that if we do not need IList semantics and can make do with ICollection the update problem can be solved by adding a reference back from Lesson to Module, such as:
public class Lesson {
...
protected virtual ICollection<Module> InModules { get; set; }
...
}
And to the mapping files add:
<class name="Lesson" table="Lessons">
...
<set name="InModules" table="ModuleToLesson">
<key column="lessonId"/>
<many-to-many column="moduleId" class="NhLists.Module, NhLists"/>
</set>
</class>
Then a Lesson deleted is also removed from the collection in Module automatically. This also works for lists but the list index is not properly updated and causes "holes" in the list.
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. =)
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");