避免锁定已在会话中的Object(UniqueObjectException)

时间:2012-08-10 11:12:15

标签: c# nhibernate

当需要再次初始化时,我会在我的对象上调用此函数:

public virtual void Initialize()
{
    if (!HibernateSessionManager.Instance.GetSession().Contains(this)) {

        try
        {
            HibernateSessionManager.Instance.GetSession()
                             .Lock(this, NHibernate.LockMode.None);
        }
        catch (NonUniqueObjectException e) { }
    }
}

我认为我可以通过检查Contains(this)来阻止初始化两次,但有时会Lock(this, NHibernate.LockMode.None)抛出NonUniqueObjectException。到目前为止,我忽略它,因为它有效,但我想知道原因和锁定我的对象的更好方法。

最好的问候,Expecto

2 个答案:

答案 0 :(得分:3)

这很可能意味着您在某处违反了身份地图。这意味着您有两个对象实例,它们具有相同的数据库ID,但具有不同的引用标识。

Session.Contains将检查引用相等性,但是如果存在任何类型相同的内容,Lock将抛出异常。 id已经在会话中,这是一个不那么严格的比较。

在AdventureWorks数据库上考虑以下测试,其中包含一个(非常天真且未经推荐的)Equals& amp;的简单实现。 GetHashCode的

 using (ISession session = SessionFactory.Factory.OpenSession())
        {
            int someId = 329;

            Person p = session.Get<Person>(someId);
            Person test = new Person() { BusinessEntityID = someId };

            Assert.IsTrue(p.Equals(test)); //your code might think the objects are equal, so you'd probably expect the next line to return true         

            Assert.IsFalse(session.Contains(test)); //But they're not the same object

            Assert.Throws<NonUniqueObjectException>(() =>
            {
                session.Lock(test, LockMode.None); //So when you ask nhibernate to track changes on both objects, it gets very confused
            });
        }

NHibernate(我猜任何ORM)通过跟踪对象的更改来工作。所以在Get'ing Person 329中,你要求NHibernate关注那个特定个人实例发生的事情。假设我们将他的名字改为Jaime。

接下来,我们得到另一个具有相同Id的人的实例(在这种情况下,我们只是新建了它,但有许多阴险的方法来获得这样的对象)。想象一下,NHibernate也会让我们将它附加到会话中。我们甚至可以将第二个对象的名字设置为Robb。

当我们刷新会话时,NHibernate无法知道数据库行是否需要同步到Robb或Jaime。因此,在可能发生之前,它会抛出非独特的方式。

理想情况下,这些情况不应该出现,但如果您非常确定发生了什么,您可能需要查看session.Merge,它可以让您将跟踪状态强制转换为最后合并的内容(Robb in例子)。

答案 1 :(得分:0)

问题完全不同 - 如果我不覆盖Equals(),则包含通过引用检查相等性。现在它适用于我的问题代码!

    public override bool Equals(object obj)
    {
        if (this == obj) { 

            return true;
        } 

        if (GetType() != obj.GetType()) {

            return false;
        }

        if (Id != ((BaseObject)obj).Id)
        {

            return false;
        }

        return true;
    }