title: Problem with one-to-many mapping using composite keys
body: <p>I have two tables in a legacy database that I need to map using NHibernate. Unfortunately, one of these tables are using a composite key and I am running into a problem which I describe below.</p>
  
  <p>Firstly, here is the schema of the two tables:</p>
  
  <pre><code>CREATE TABLE [dbo].[tBenchmarkFxHedgeHistory](
      [BenchmarkFxHedgeHistoryId] [int] IDENTITY(1,1) NOT NULL,
      [BenchmarkFxHedgeId] [int] NOT NULL,
      [ModelId] [int] NOT NULL,
      [BaseCurrencyCode] [nvarchar](5) NOT NULL,
      [BenchmarkFxHedgeTypeId] [int] NOT NULL,
      [DateFrom] [smalldatetime] NOT NULL,
      [DateTo] [smalldatetime] NULL,
      [PctHedgeBackToBase] [decimal](13, 10) NULL,
      [Enabled] [bit] NOT NULL,
      [BenchmarkHedgeStatusId] [int] NOT NULL,
      [AuditActionId] [int] NOT NULL,
      [Timestamp] [timestamp] NOT NULL,
      [HistoryUser] [nvarchar](50) NOT NULL CONSTRAINT [DF_tBenchmarkFxHedgeHistory_HistoryUser]  DEFAULT (suser_sname()),
      [HistoryDate] [datetime] NOT NULL CONSTRAINT [DF_tBenchmarkFxHedgeHistory_HistoryDate]  DEFAULT (getdate()),
   CONSTRAINT [PK_tBenchmarkFxHedgeHistory] PRIMARY KEY CLUSTERED 
  (
      [BenchmarkFxHedgeHistoryId] ASC
  )
  
  CREATE TABLE [dbo].[tBenchmarkFxHedgeRatio](
      [BenchMarkFxHedgeId] [int] NOT NULL,
      [NonBaseCurrencyCode] [nvarchar](5) NOT NULL,
      [PctHedgeBackToBase] [decimal](13, 10) NOT NULL,
   CONSTRAINT [PK_tBenchmarkFxHedgeRatio] PRIMARY KEY CLUSTERED 
  (
      [BenchMarkFxHedgeId] ASC,
      [NonBaseCurrencyCode] ASC
  )
  </code></pre>
  
  <p>And here are the domain classes:</p>
  
  <pre><code>public class BenchmarkFxHedgeRuleHistory 
  {
      private IList&lt;BenchmarkFxRuleRatioHistory&gt; _percentages = new List&lt;BenchmarkFxRuleRatioHistory&gt;();
  
      public virtual int Id { get; set; }
      public virtual string BaseCurrencyCode { get; set; }
      public virtual DateTime DateFrom { get; set; }
      public virtual DateTime? DateTo { get; set; }
      public virtual decimal? Percentage { get; set; }
      public virtual bool Enabled { get; set; }
      public virtual byte[] Timestamp { get; set; }
      public virtual BenchmarkFxHedgeStatus Status { get; set; }
      public virtual BenchmarkFxHedgeType Strategy { get; set; }
      public virtual Model Model { get; set; }
      public virtual BenchmarkFxHedgeRule Rule { get; set; }
      public virtual AuditAction AuditAction { get; set; }
  
      public virtual IList&lt;BenchmarkFxRuleRatioHistory&gt; Percentages
      {
          get { return _percentages; }
      }
  }
  
  [Serializable]
  public class BenchmarkFxRuleRatioHistory
  {
      public virtual string NonBaseCurrencyCode { get; set; }
      public virtual decimal Percentage { get; set; }
      public virtual BenchmarkFxHedgeRuleHistory HistoryEntry { get; set; }
  
      public override bool Equals(object obj)
      {
          var rule = obj as BenchmarkFxRuleRatioHistory;
          if (rule == null) return false;
          return rule.HistoryEntry.Id == HistoryEntry.Id &amp;&amp; NonBaseCurrencyCode == rule.NonBaseCurrencyCode;
      }
  
      public override int GetHashCode()
      {
          return NonBaseCurrencyCode.GetHashCode() ^ HistoryEntry.GetHashCode();
      }
  }
  </code></pre>
  
  <p>And finally, here are the NHibernate mapping files:</p>
  
  <pre><code>&lt;?xml version="1.0" encoding="utf-8"?&gt;
  &lt;hibernate-mapping namespace="Tests.DomainModel" assembly="Tests.DomainModel" xmlns="urn:nhibernate-mapping-2.2"&gt;
      &lt;class name="BenchmarkFxHedgeRuleHistory" table="`tBenchmarkFxHedgeHistory`" schema="`dbo`"&gt;
          &lt;id name="Id" access="property" column="`BenchmarkFxHedgeHistoryId`"&gt;
              &lt;generator class="native" /&gt;
          &lt;/id&gt;
      &lt;many-to-one name="Rule" class="BenchmarkFxHedgeRule" column="`BenchmarkFxHedgeId`" not-null="true" fetch="select" /&gt;
          &lt;property name="BaseCurrencyCode" type="String" column="`BaseCurrencyCode`" length="5" /&gt;
          &lt;property name="DateFrom" type="DateTime" column="`DateFrom`" /&gt;
          &lt;property name="DateTo" type="DateTime" column="`DateTo`" /&gt;
          &lt;property name="Enabled" type="Boolean" column="`Enabled`" /&gt;
          &lt;property name="Percentage" type="Decimal" column="`PctHedgeBackToBase`" /&gt;
          &lt;property name="Timestamp" type="BinaryBlob" column="`Timestamp`" /&gt;
          &lt;many-to-one name="Status" class="BenchmarkFxHedgeStatus" column="`BenchmarkHedgeStatusId`" not-null="true" fetch="join" /&gt;
      &lt;many-to-one name="Strategy" class="BenchmarkFxHedgeType" column="`BenchmarkFxHedgeTypeId`" not-null="true" fetch="join" /&gt;
          &lt;many-to-one name="Model" class="Model" column="`ModelId`" not-null="true" fetch="select" /&gt;
      &lt;many-to-one name="AuditAction" class="AuditAction" column="`AuditActionId`" not-null="true" fetch="join" /&gt;
      &lt;bag name="Percentages" fetch="join" access="readonly" inverse="true" lazy="false" table="tBenchmarkFxHedgeRatioHistory" cascade="all-delete-orphan" subselect=""&gt;
        &lt;key column="`BenchmarkFxHedgeHistoryId`" /&gt;
        &lt;one-to-many class="BenchmarkFxRuleRatioHistory" /&gt;
      &lt;/bag&gt;
      &lt;/class&gt;
  &lt;/hibernate-mapping&gt;
  
  &lt;?xml version="1.0" encoding="utf-8"?&gt;
  &lt;hibernate-mapping namespace="Tests.DomainModel" assembly="Tests.DomainModel" xmlns="urn:nhibernate-mapping-2.2"&gt;
      &lt;class name="BenchmarkFxRuleRatioHistory" table="`tBenchmarkFxHedgeRatioHistory`" schema="`dbo`"&gt;
      &lt;composite-id&gt;
        &lt;key-property name="NonBaseCurrencyCode" type="String" column="`NonBaseCurrencyCode`" /&gt;
        &lt;key-many-to-one name="HistoryEntry" class="BenchmarkFxHedgeRuleHistory" column="`BenchmarkFxHedgeHistoryId`" /&gt;
      &lt;/composite-id&gt;
      &lt;property name="Percentage" type="decimal" column="`PctHedgeBackToBase`" /&gt;
      &lt;/class&gt;
  &lt;/hibernate-mapping&gt;
  </code></pre>
  
  <p>Now I use these in the following code:</p>
  
  <pre><code>using(var session = DataMapperConfiguration.SessionFactory.OpenSession())
  {
      var sessionRule = session.Get&lt;BenchmarkFxHedgeRule&gt;(id);
      var historyList = session.Query&lt;BenchmarkFxHedgeRuleHistory&gt;()
          .Where(x =&gt; x.Rule == sessionRule).ToList();
      Assert.AreEqual(2, historyList[0].Percentages.Count);
  }
  </code></pre>
  
  <p>What happens is that <code>sessionRule</code> is correctly hydrated, but <code>historyList</code> does not have its <code>Percentages</code> property correctly hydrated. It keeps coming back as an empty list, but I expect it to have members in the list since there are matching rows in the database.</p>
  
  <p>Any advice? What could I be doing wrong?</p>