I am involved in a project migrating our custom DAL layer over to NHibernate. I'm having a problem with regards to the SQL my NHibernate mapping files are generating.
I have an inheritance hierarchy of three classes, which maps to two tables as a <joined-subclass>. The lowest level child class has a collection that maps to another table, as a <bag>. See below for full details.
The issue is the SQL NHibernate creates selects the collection using the wrong key from the parent associated object.
Any help greatly appreciated
Hibernate version:
Quote:
NHibernate 1.2.1.4000
Mapping documents:
Code:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping
xmlns="urn:nhibernate-mapping-2.2"
default-cascade="none"
auto-import="true"
namespace="BakerTilly.CompositeReporting.Domain.Entities"
assembly="BakerTilly.CompositeReporting.Domain.Entities">
<class name="Entity`1[[BakerTilly.CompositeReporting.Domain.Entities.Organisation]]" table="Entity" lazy="false">
<id name="EntityID" column="ID" type="Int32" unsaved-value="-1">
<generator class="identity" />
</id>
<property name="EntityType" column="EntityTypeID" />
<joined-subclass name="Organisation" table="Organisation" lazy="false">
<key column="EntityID" ></key>
<property name="Id" column="ID" generated="always" />
<property name="ParentID" column="ParentID" />
<property name="Name" column="Name" />
<property name="FullName" column="FullName" />
<property name="Code" column="Code" />
<property name="CurrencyID" column="CurrencyID" />
<property name="Editable" column="Editable" />
<property name="AccountsYearEnd" column="AccountsYearEnd" />
<property name="Sector" column="SectorID" />
<property name="ImportType" column="ImportTypeID" />
<property name="EntityID" column="EntityID" />
<property name="Theme" column="Theme" />
<bag name="Attributes" table="OrganisationAttribute" lazy="false" cascade="all" fetch="select">
<key column="OrganisationID" foreign-key="OrganisationID"></key>
<one-to-many class="OrganisationAttribute"></one-to-many>
</bag>
</joined-subclass>
</class>
</hibernate-mapping>
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping
xmlns="urn:nhibernate-mapping-2.2"
default-cascade="none"
auto-import="true"
namespace="BakerTilly.CompositeReporting.Domain.Entities" assembly="BakerTilly.CompositeReporting.Domain.Entities">
<class name="OrganisationAttribute" table="OrganisationAttribute" lazy="false">
<id name="Id">
<generator class="native"/>
</id>
<property name="OrganisationId"></property>
<property name="Name"></property>
<property name="Ordinal"></property>
</class>
</hibernate-mapping>
Code between sessionFactory.openSession() and session.close():Code:
ISession session = NHibernateHelper.GetCurrentSession();
ICriteria criteria = session.CreateCriteria(typeof(Organisation));
criteria.Add(Expression.Eq("Id", organisationID));
IOrganisation org = criteria.UniqueResult<IOrganisation>();
NHibernateHelper.CloseSession();
Full stack trace of any exception that occurs:None occuring
Name and version of the database you are using:SQL Server 2008 Stadard Edition
The generated SQL (show_sql=true):This is grabbed from SQL Query Profiler.
In the first query 17 is the primary key of the row from the Organisation table
In the second query 32 is the primary key of the row from the Entity table, but I really want to pass in 17 as this is the foreign key value
Code:
exec sp_executesql N'SELECT this_.EntityID as ID2_0_, this_.ID as ID3_0_, this_.ParentID as ParentID3_0_, this_.Name as Name3_0_, this_.FullName as FullName3_0_, this_.Code
as Code3_0_, this_.CurrencyID as CurrencyID3_0_, this_.Editable as Editable3_0_, this_.AccountsYearEnd as Accounts9_3_0_, this_.SectorID as SectorID3_0_, this_.ImportTypeID
as ImportT11_3_0_, this_.EntityID as EntityID3_0_, this_.Theme as Theme3_0_, this_1_.EntityTypeID as EntityTy2_2_0_ FROM Organisation this_ inner join Entity this_1_ on
this_.EntityID=this_1_.ID WHERE this_.ID = @p0',N'@p0 int',@p0=17
exec sp_executesql N'SELECT attributes0_.OrganisationID as Organisa5___1_, attributes0_.Id as Id1_, attributes0_.Id as Id1_0_, attributes0_.OrganisationId as
Organisa2_1_0_, attributes0_.Name as Name1_0_, attributes0_.Ordinal as Ordinal1_0_ FROM OrganisationAttribute attributes0_ WHERE attributes0_.OrganisationID=@p0',N'@p0
int',@p0=32
C# CodeCode:
using System;
using System.Collections.Generic;
namespace BakerTilly.CompositeReporting.Domain.Entities
{
/// <summary>
/// Contains all the information for an Organisation
/// </summary>
[Serializable]
public class Organisation : Entity<Organisation>, IOrganisation
{
private int parentID;
private string name;
private string fullName;
private string code;
private int currencyID;
private bool editable;
private DateTime accountsYearEnd;
private Sector sector;
private ImportType importType;
private string theme;
//private OrganisationAttributeCollection attributes;
private IList<OrganisationAttribute> attributes;
private OrganisationAttributeUsageCollection attributeUsage;
/// <summary>
/// Initializes a new instance of the <see cref="Organisation"/> class.
/// </summary>
public Organisation()
{
}
/// <summary>
/// Gets the type of the entity.
/// </summary>
/// <value>The type of the entity.</value>
public override EntityTypes EntityType { get; set; }
/// <summary>
/// Gets or sets the parent ID.
/// </summary>
/// <value>The parent ID.</value>
public int ParentID
{
get { return this.parentID; }
set { this.parentID = value; }
}
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public string Name
{
get { return this.name; }
set { this.name = value; }
}
/// <summary>
/// Gets or sets the full name.
/// </summary>
/// <value>The full name.</value>
public string FullName
{
get { return this.fullName; }
set { this.fullName = value; }
}
/// <summary>
/// Gets the type.
/// </summary>
/// <value>The type.</value>
public OrganisationType Type
{
get
{
if (this.parentID > 0)
{
return OrganisationType.Child;
}
else
{
return OrganisationType.Parent;
}
}
}
/// <summary>
/// Gets or sets the currency ID.
/// </summary>
/// <value>The currency ID.</value>
public int CurrencyID
{
get { return this.currencyID; }
set { this.currencyID = value; }
}
/// <summary>
/// Gets or sets a value indicating whether this <see cref="Organisation"/> is editable.
/// </summary>
/// <value><c>true</c> if editable; otherwise, <c>false</c>.</value>
public bool Editable
{
get { return this.editable; }
set { this.editable = value; }
}
/// <summary>
/// Gets or sets the accounts year end.
/// </summary>
/// <value>The accounts year end.</value>
public DateTime AccountsYearEnd
{
get {return this.accountsYearEnd ;}
set { this.accountsYearEnd = value; }
}
/// <summary>
/// Gets or sets the sector.
/// </summary>
/// <value>The sector.</value>
public Sector Sector
{
get { return this.sector; }
set { this.sector = value; }
}
/// <summary>
/// Gets or sets the type of the import.
/// </summary>
/// <value>The type of the import.</value>
public ImportType ImportType
{
get { return this.importType; }
set { this.importType = value; }
}
/// <summary>
/// Gets or sets the theme.
/// </summary>
/// <value>The theme.</value>
public string Theme
{
get { return this.theme; }
set { this.theme = value; }
}
/// <summary>
/// Gets or sets the code.
/// </summary>
/// <value>The code.</value>
public string Code
{
get { return this.code; }
set { this.code = value; }
}
/// <summary>
/// Gets or sets the attributes.
/// </summary>
/// <value>The attributes.</value>
public IList<OrganisationAttribute> Attributes
{
get { return this.attributes; }
set { this.attributes = value; }
}
/// <summary>
/// Gets or sets the attribute usage.
/// </summary>
/// <value>The attribute usage.</value>
public OrganisationAttributeUsageCollection AttributeUsage
{
get { return this.attributeUsage; }
set { this.attributeUsage = value; }
}
}
/// <summary>
/// Contains all the information for an Entity
/// Read Entity as Contactable Entity
/// </summary>
[Serializable]
public abstract class Entity<EntityT> : BaseDomainEntity<EntityT>, BakerTilly.CompositeReporting.Domain.IEntity
{
private int entityID;
private ContactDetailCollection contactDetails;
/// <summary>
/// Gets the type of the entity.
/// </summary>
/// <value>The type of the entity.</value>
public abstract EntityTypes EntityType { get; set; }
/// <summary>
/// Gets or sets the entity ID.
/// </summary>
/// <value>The entity ID.</value>
public int EntityID
{
get { return this.entityID; }
set { this.entityID = value; }
}
/// <summary>
/// Initializes a new instance of the <see cref="Entity<EntityT>"/> class.
/// </summary>
public Entity()
{
}
/// <summary>
/// The base class for all domain entity objects
/// </summary>
/// <typeparam name="TDomainEntity">The type of the domain entity.</typeparam>
[Serializable]
public abstract class BaseDomainEntity<TDomainEntity>
{
private int id;
private RecordStatus recordStatus;
/// <summary>
/// Default class constructor
/// </summary>
protected BaseDomainEntity()
{
this.id = DomainEntityConstants.NullIdInt32Value;
this.recordStatus = RecordStatus.Available;
}
}
Class diagram
Database diagram
Database data
