ユーザーが注文を開いて変更を加え、データベースに保存できるシンプルなアプリケーションがあります。注文が開かれると、このようにデータベースから注文を取得します
ISession session = SessionFactory.OpenSession();
...
Order order = session.Query<Order>()
.Where(o => o.Id == id)
.FirstOrDefault();
order.Commentsの遅延読み込みを有効にするために、注文が閉じられるまでセッションを開いたままにします。マッピングは次のとおりです。
<class name="Order">
<id name="Id">
<generator class="identity" />
</id>
...
<set name="Comments" access="field.camelcase-underscore" cascade="all-delete-orphan" lazy="true" order-by="Created">
<key column="OrderId" />
<one-to-many class="Comment" />
</set>
</class>
<class name="Comment" table="OrderComment" lazy="false">
<id name="Id">
<generator class="identity" />
</id>
<many-to-one name="Author" />
<property name="Created" />
<property name="Text" length="1000" />
</class>
このアプリは、注文が開いている間、閉じる前に数回保存できるように設計されています。私はこのように保存します:
using (ITransaction trans = session.BeginTransaction())
{
session.SaveOrUpdate(order)
trans.Commit();
}
最後に、ユーザーが注文を閉じると、セッションを破棄します。
ここに問題があります。ユーザーがコメントを追加して保存し、注文を閉じる前に別のコメントを追加して再度保存すると、2回目の保存中に最初のコメントが削除されます。2番目の保存から出力されるSQLは次のとおりです。
NHibernate: INSERT INTO OrderComment (Id, Author, Created, Text) VALUES (hibernate_sequence.nextval, :p0, :p1, :p2) returning Id into :nhIdOutParam;:p0 = 1 [Type: Int32 (0)], :p1 = 14.01.2013 12:53:20 [Type: DateTime (0)], :p2 = '2' [Type: String (0)], :nhIdOutParam = NULL [Type: Int32 (0)]
**NHibernate: UPDATE OrderComment SET OrderId = null WHERE OrderId = :p0 AND Id = :p1;:p0 = 465 [Type: Int32 (0)], :p1 = 591 [Type: Int32 (0)]**
NHibernate: UPDATE OrderComment SET OrderId = :p0 WHERE Id = :p1;:p0 = 465 [Type: Int32 (0)], :p1 = 592 [Type: Int32 (0)]
したがって、問題は太字の行です。最初のコメントのOrderIdがnullに設定されています。誰かが私の理由を教えてもらえますか?
ここでnHibernateを使用する方法に何か問題がありますか?私がしていることを繰り返すには:
- セッションを開く
- オブジェクトを取得する
- ユーザーがオブジェクトを更新します
- トランザクションを開始し、session.SaveOrUpdate(object)を呼び出してから、トランザクションをコミットして、オブジェクトを保存します。
- 手順3と4を何度でも繰り返します。
- セッションを破棄します。
これは許容できる使用方法nHibernate
ですか?
編集
Orderクラスのcommentsプロパティは次のとおりです。
ICollection<Comment> _comments = new List<Comment>();
public virtual ReadOnlyCollection<Comment> Comments
{
get
{
return _comments.ToList().AsReadOnly();
}
}
次に、次のメソッドを呼び出してコメントを追加します。
public virtual void AddComment(Comment comment)
{
_comments.Add(comment);
}
...
Comment comment = new Comment()
{
Author = User.Current,
Created = DateTime.Now,
Text = text
};
order.AddComment(comment);
これがCommentクラスです。Idは基本クラスに実装されていますPersistentObject<T>
:
public class Comment : PersistentObject<int>
{
public User Author { get; private set; }
public DateTime Created { get; private set; }
public string Text { get; private set; }
}
public abstract class PersistentObject<T>
{
public virtual T Id { get; protected internal set; }
public override bool Equals(object obj)
{
// If both objects have not been saved to database, then can't compare Id because this
// will be 0 for both. In this case use reference equality.
PersistentObject<T> other = obj as PersistentObject<T>;
if (other == null)
return false;
bool thisIsDefault = object.Equals(Id, default(T));
bool otherIsDefault = object.Equals(other.Id, default(T));
if (thisIsDefault && otherIsDefault)
return object.ReferenceEquals(this, other);
else if (thisIsDefault || otherIsDefault)
return false;
else
return object.Equals(this.Id, other.Id);
}
public override int GetHashCode()
{
return Id.GetHashCode();
}
}