4

I've referenced the following thread which works with a Session but not a StatelessSession. NHibernate Eager Fetching Over Multiple Levels

The problem I am having is similar to the above thread. I'm getting back 4xApplications and 4xRoles. I should be getting back 1xApplication and 4xRoles.

Criteria query:

return StatelessSession.CreateCriteria(typeof(Application))
            .SetResultTransformer(Transformers.DistinctRootEntity)
            .SetFetchMode("Roles", FetchMode.Join)
            .List<Application>();

My entities and mappings:

public class Application : IComparable<Application>
{
    public virtual int Id { get;  set; }
    public virtual string InternalName { get; set; }
    public virtual ICollection<Role> Roles { get; set; }

    #region IComparable<Application> Members

    public virtual int CompareTo(Application other)
    {
        return Id.CompareTo(other.Id);
    }

    #endregion

}

public class Role : IComparable<Role>
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual Application Application { get; set; }

    public virtual int CompareTo(Role other)
    {
        return Id.CompareTo(other.Id);
    }

}

public class ApplicationMapping : ClassMap<Application>
{
    public ApplicationMapping()
    {
        Table("Application");
        Id(x => x.Id, "ID").GeneratedBy.Assigned(); ; //.Column("ID");
        Map(x => x.InternalName);
        HasMany(x => x.Roles).Inverse().Cascade.All().AsSet().Not.LazyLoad();
    }
}

public class RoleMapping : ClassMap<Role>
{
    public RoleMapping()
    {

        Table("Roles");
        Id(x => x.Id, "ID").GeneratedBy.Assigned(); 
        References(x => x.Application, "ApplicationID").Not.LazyLoad();
        Map(x => x.Name);
        HasMany(x => x.RightsUsers).Table("UserRoleXRef").Inverse().Cascade.All().AsSet();
    }   
}

hbm.xml

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property"     auto-import="true" default-cascade="none" default-lazy="true">
  <class xmlns="urn:nhibernate-mapping-2.2" name="Quad.App.Test.DataAccess.NHibernate.Scaffolding.Entities.Application, Quad.App.Test, Version=1.4.0.742, Culture=neutral, PublicKeyToken=null" table="Application">
<id name="Id" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
  <column name="ID" />
  <generator class="assigned" />
</id>
<property name="InternalName" type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
  <column name="InternalName" />
</property>
<set cascade="all" inverse="true" lazy="false" name="Roles">
  <key>
    <column name="ApplicationID" />
  </key>
  <one-to-many class="Quad.App.Test.DataAccess.NHibernate.Scaffolding.Entities.Role, Quad.App.Test, Version=1.4.0.742, Culture=neutral, PublicKeyToken=null" />
</set>
   </class>
</hibernate-mapping>

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property"     auto-import="true" default-cascade="none" default-lazy="true">
  <class xmlns="urn:nhibernate-mapping-2.2" name="Quad.App.Test.DataAccess.NHibernate.Scaffolding.Entities.Role, Quad.App.Test, Version=1.4.0.742, Culture=neutral, PublicKeyToken=null" table="Roles">
<id name="Id" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
  <column name="ID" />
  <generator class="assigned" />
</id>
<property name="Name" type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
  <column name="Name" />
</property>
<many-to-one class="Quad.App.Test.DataAccess.NHibernate.Scaffolding.Entities.Application, Quad.App.Test, Version=1.4.0.742, Culture=neutral, PublicKeyToken=null" lazy="false" name="Application">
  <column name="ApplicationID" />
</many-to-one>
  </class>
</hibernate-mapping>

SQL:

SELECT this_.ID as ID1_1_, 
this_.InternalName as Internal2_1_1_, 
roles2_.ApplicationID as Applicat3_3_, 
roles2_.ID as ID3_, 
roles2_.ID as ID2_0_, 
roles2_.Name as Name2_0_, 
roles2_.ApplicationID as Applicat3_2_0_ 
FROM dbo.Application this_ left outer join dbo.Roles roles2_ on       
this_.ID=roles2_.ApplicationID
Community
  • 1
  • 1
magellings
  • 175
  • 2
  • 10
  • weird. Do you realize you already have Roles mapped as non-lazy? I wonder if FetchMode doesn't like being told twice? If you remove the SetFetchMode, how does it non-lazily fetch the Roles? outer join or select? – dotjoe Feb 23 '10 at 22:05
  • Then I get an exception that you can't lazily load the list of Roles. It just selects from the Application table. SELECT this_.ID as ID1_0_, this_.InternalName as Internal2_1_0_ FROM dbo.Application this_ {"Initializing[Quad.App.Test.DataAccess.NHibernate.Scaffolding.Entities.Application#50]-failed to lazily initialize a collection of role: Quad.App.Test.DataAccess.NHibernate.Scaffolding.Entities.Application.Roles, no session or session was closed"} – magellings Feb 25 '10 at 19:45

1 Answers1

0

I suspect this is because the sql query generated produces duplicate applications as the roles were joined.

The simplest way to avoid this is to use an ISet (HashedSet<> implementation) instead of an ICollection (defined in Iesi.Collections.dll), these are designed so you will not get duplicates in the list.

Liath
  • 9,913
  • 9
  • 51
  • 81