1

NHibernateで次のテストに合格するにはどうすればよいですか?

これが私が望むように機能するには、エンティティクラスのEqualsとGetHashCodeを単純にオーバーライドするだけで十分だと思いました。明らかに、非常に些細な「ポイント」オブジェクトの場合、同じ座標に対して複数の行を永続化するのはばかげています。同一の座標を持つ2つのポイントオブジェクトがあり、それらをデータベースの1つの行にのみ保持する必要があります。

    Point p1 = new Point(1, 1, 1);
    Point p2 = new Point(1, 1, 1);
    Assert.AreEqual(p1, p2); //Passes
    session.Save(p1);
    session.Save(p2);
    tx.Commit();
    IList<Point> points = session.CreateCriteria<Point>()
        .List<Point>();
    Assert.AreEqual(1,points.Count); //FAILS

私のポイントクラスは次のようになります。

public class Point
{
    public virtual Guid Id { get; set; }
    public virtual double X { get; set; }
    public virtual double Y { get; set; }
    public virtual double Z { get; set; }

    public Point(double x, double y, double z)
    {
        X = x; Y = y; Z = z;
    }
    public override bool Equals(object obj)
    {
        Point you = obj as Point;
        if (you != null)
            return you.X == X && you.Y == Y && you.Z == Z;
        return false;
    }

    public override int GetHashCode()
    {
        int hash = 23;
        hash = hash * 37 + X.GetHashCode();
        hash = hash * 37 + Y.GetHashCode();
        hash = hash * 37 + Z.GetHashCode();
        return hash;
    }
}
4

4 に答える 4

3

あなたは間違った角度から問題に取り組んでいると思います。

これが実際のドメインである場合は、別のテーブルではなく、ポイントに Components または UserTypes を使用する必要があります。ポイントには明らかに値型のセマンティクスがあります。

http://nhibernate.info/doc/nh/en/index.html#componentshttp://nhibernate.info/doc/nh/en/index.html#mapping-types-customを読む

于 2010-03-04T01:47:42.900 に答える
2

NHibernate は、例の 2 つの Point インスタンスを同じとして認識しません。ID が異なるためです。Points テーブルの主キーとして GUID を使用しているようで、作成する各 Point には異なる GUID があります。

ここで説明されているように、あなたが探しているのは複合 ID と呼ばれるものだと思います。ただし、NHibernate のマニュアルには、複合キーはレガシー データベースをサポートするためだけに存在すると書かれていることに注意してください。複合キーは使用しないことを強くお勧めます

「残念ながら、複合識別子へのこのアプローチは、永続オブジェクトが独自の識別子であることを意味します。オブジェクト自体以外に便利な「ハンドル」はありません。ロードする前に、永続クラス自体のインスタンスをインスタンス化し、その識別子プロパティを設定する必要があります。 () 複合キーに関連付けられた永続的な状態。"

代わりに、マニュアルでは、コンポーネントを複合識別子として使用することを提案しています。

個人的には、GUID をそのまま維持し、アプリケーション レイヤーにロジックを追加して、データベースで強制するのではなく、ポイントの重複を防止することを検討します。ただし、それはすべてアプリケーションの個々のニーズに依存します。

于 2010-03-04T05:32:30.330 に答える
1

通常、NHibernateはオブジェクトがそのIDによって保存されているかどうかを判断します。

したがって、Id属性を実装して、すべての「等しい」オブジェクトに対して同じIDを返す場合は、問題ないはずです(私は思います)。

したがって、これでテストに合格するかどうか試してください(マッピングに適切な変更を加えてください)。

public class Point
{
    public virtual int Id { 
        get { this.GetHashCode() }
    }
// the rest

HashCodeはこの方法で一意であることが保証されていないため、Idに他の値を使用することもできます。

于 2010-03-04T00:09:10.023 に答える
0

これは、(1, 1, 1) 座標を持つすべてのポイントを 1 つだけと見なすことを意味します。NHibernate のプラクティスでは推奨されていませんが、composite-id を使用して行うことができます。

このリンク: Chapter 7. Component Mappingをたどりながら、composite-id が説明されている 7.4 までスクロールします。次に、マッピングは次のようになります。

<class="Point" tables="POINTS">
  <composite-id name="CompId" class="PointCoord">
    <key-property name="X" type="System.Double">
    <key-property name="Y" type="System.Double">
    <key-property name="Z" type="System.Double">
  </composite-id>
  <property name="Id" type="System.Guid">
</class>

そして、あなたのクラスは次のように分割する必要があります:

public class PointCoord {
    public double X { get; set; }
    public double Y { get; set; }
    public double Z { get; set; }
}

public class Point : PointCoord {  
    public Guid Id { get; set; }
}

そうすれば、Guid はもはや Id ではないため、クラスの Id として保持する必要はありません。あなたの Id はあなたの座標 X、Y、Z になりました。次に、それに応じて Equals() メソッドをオーバーライドする必要があります。

public override bool Equals(object obj) {
    if (obj == null)
        return false;

    if (((Point)obj) == null) 
        return false;

    Point p = (Point)obj;

    return this.X == p.X && this.Y == p.Y && this.Z == p.Z
}

ところで、クラスの型を入力パラメーターとして受け取るこのメソッドのオーバーロードを常に用意しておくことをお勧めします。これにより、パフォーマンスが向上します。

public bool Equals(Point pt) {
    if (pt == null) 
        return false;

    return this.X == pt.X && this.Y == pt.Y && this.Z == pt.Z
}

ただし、これは通常は良い方法とは見なされず、NHibernate はすべてのテーブルに独自の DB ID を持たせることを強くお勧めします。この ID は、たとえば請求書番号などの重要なドメイン値であってはなりません。請求書番号と DB ID が表示されます。この複合 ID は、従来の互換性のために保持されています。

于 2010-03-04T06:15:04.170 に答える