我有一个简单的应用程序,用户可以在其中打开订单,对其进行更改,然后将它们保存回数据库。打开订单后,我会像这样从数据库中检索它
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();
}
最后,当用户关闭订单时,我处理了会话。
这里有问题:如果用户添加评论,保存,然后在关闭订单之前添加另一个评论并再次保存,则在第二次保存期间删除第一个评论。这是第二次保存输出的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 被设置为空。谁能告诉我为什么?
我在这里使用 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);
这是评论类。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();
}
}