ISession.Get(id) を使用して、特定の顧客の全体を取得したい Customer-Order-Line という 3 レベルのエンティティ階層があります。次の XML フラグメントがあります。
customer.hbm.xml:
<bag name="Orders" cascade="all-delete-orphan" inverse="false" fetch="join">
<key column="CustomerID" />
<one-to-many class="Order" />
</bag>
order.hbm.xml:
<bag name="Lines" cascade="all-delete-orphan" inverse="false" fetch="join">
<key column="OrderID" />
<one-to-many class="Line" />
</bag>
fetch="join" 属性を使用して、各親の子エンティティをフェッチすることを示しました。これにより、正しい SQL が構築されました。
SELECT
customer0_.ID AS ID8_2_,
customer0_.Name AS Name8_2_,
orders1_.CustomerID AS CustomerID__4_,
orders1_.ID AS ID4_,
orders1_.ID AS ID9_0_,
orders1_.PostalAddress AS PostalAd2_9_0_,
orders1_.OrderDate AS OrderDate9_0_,
lines2_.OrderID AS OrderID__5_,
lines2_.ID AS ID5_,
lines2_.ID AS ID10_1_,
lines2_.[LineNo] AS column2_10_1_,
lines2_.Quantity AS Quantity10_1_,
lines2_.ProductID AS ProductID10_1_
FROM Customer customer0_
LEFT JOIN [Order] orders1_
ON customer0_.ID=orders1_.CustomerID
LEFT JOIN Line lines2_
ON orders1_.ID=lines2_.OrderID
WHERE customer0_.ID=1
これまでのところ、これは問題ないようです。SQL は正しいレコードのセット (1 つの異なる orderid のみ) を返しますが、テストを実行して Orders と Lines の (NH からの) エンティティの正しい数を確認すると、間違った結果が得られます。
(テスト データから) 1xOrder と 4xLineを取得する必要がありますが、4xOrder と 4xLine を取得しています。NH は、結果セット内の Order 情報の「繰り返し」グループを認識しておらず、Order エンティティを正しく「再利用」していないようです。
私はすべての整数 ID (PK) を使用しており、NH がこれらのエンティティの同等性を確認することを期待して、この ID を使用して T の IComparable と T の IEquatable を実装しようとしました。また、ID を使用するために Equals と GetHashCode をオーバーライドしようとしました。これらの「試み」はいずれも成功していません。
「複数レベルのフェッチ」は NH でサポートされている操作ですか? もしそうなら、それをサポートするために必要な XML 設定 (またはその他のメカニズム) はありますか?
NB: sirocco のソリューションを使用して、自分のコードにいくつかの変更を加えて、最終的にこれを解決しました。xml は、すべてのコレクションに対してバッグからセットに変更する必要があり、エンティティ自体は、一意性を確立するためのセットの要件である IComparable<> を実装するように変更されました。
public class BaseEntity : IComparable<BaseEntity>
{
...
private Guid _internalID { get; set; }
public virtual Guid ID { get; set; }
public BaseEntity()
{
_internalID = Guid.NewGuid();
}
#region IComparable<BaseEntity> Members
public int CompareTo( BaseEntity other )
{
if ( ID == Guid.Empty || other.ID == Guid.Empty )
return _internalID.CompareTo( other._internalID );
return ID.CompareTo( other.ID );
}
#endregion
...
}
InternalID フィールドの使用に注意してください。これは、新しい (一時的な) エンティティに必要です。そうしないと、最初は ID がありません (私のモデルでは、保存時に ID が提供されます)。