NHibernate:持久化对象时出现意外的会话问题
我在使用一些 NHibernate 代码时遇到了问题。基本上,我试图使会话生命周期尽可能短,以最大限度地减少应用程序中的状态信息。
我发现在不具体的情况下很难描述我的问题,所以我将继续使用文章和类别隐喻。
public class Article
{
public virtual long Id { get; set; }
public virtual string Name { get; set; }
public virtual Category Category { get; set; }
}
public class Category
{
public virtual long Id { get; set; }
public virtual string Name { get; set; }
public override string ToString() { return "cat-" + this.Id.ToString(); }
}
public class ArticleMapping : ClassMap<Article>
{
public ArticleMapping()
{
this.Id( x => x.Id, "id" ).GeneratedBy.Assigned();
this.Map( x => x.Name, "name" );
this.References( x => x.Category ).Fetch.Join().Cascade.None();
}
}
public class CategoryMapping : ClassMap<Category>
{
public CategoryMapping()
{
this.ReadOnly();
this.Id(x => x.Id, "id").GeneratedBy.Assigned();
this.Map(x => x.Name, "name");
}
}
我所做的是创建一个新的文章,为其命名,从通过下面描述的方法加载的列表中分配一个类别并尝试保存。
我收到以下警告:
无法确定具有指定标识符的 cat-1 是瞬态还是分离
如果您查看模型和映射代码,您会发现 cat-1 根本不应该被持久化 - 它是一个类别并定义为只读和非-级联。
//code that loads the list of categories
IStatelessSession session = this.SessionService.GetStatelessSession();
IList<Category> cats = session.CreateCriteria<Category>().List<Category>();
this.SessionService.EndSession(session);
//code that's called to save the instance
ISession session = this.SessionService.GetSession();
using (ITransaction transaction = session.BeginTransaction())
{
session.SaveOrUpdate(article);
transaction.Commit();
}
this.SessionService.EndSession(session);
如果我再次调用 Save 方法,一切都会变成香蕉状:
异常:
NHibernate.StaleStateException:意外的行计数:0;预期:1
Stacktrace:
在 NHibernate.AdoNet.Expectations.BasicExpectation.VerifyOutcomeNonBatched(Int32 rowCount,IDbCommand 语句) 在 NHibernate.AdoNet.NonBatchingBatcher.AddToBatch(IExpectation 期望)
在 NHibernate.Persister.Entity.AbstractEntityPersister.Update(Object id, Object[] fields, [] oldFields、对象 rowId、布尔 [] includeProperty、Int32 j、对象 oldVersion、对象 obj、SqlCommandInfo sql、ISessionImplementor 会话)
NHibernate.Persister.Entity.AbstractEntityPersister.UpdateOrInsert(对象 id,对象 [] 字段,对象 [] 处的对象 oldFields、对象 rowId、布尔值 [] includeProperty、Int32 j、对象oldVersion、Object obj、SqlCommandInfo sql、ISessionImplementor 会话)
位于 NHibernate.Persister.Entity.AbstractEntityPersister.Update(对象 id、Object[] fields、Int32[] dirtyFieldA 类型“NHibernate.StaleStateException”的第一次机会异常出现在 NHibernate.dll 中 s,布尔值 hasDirtyCollection,Object[] oldFields,对象 oldVersion,对象 obj,对象 rowId,ISessionImplementor 会话)
在 NHibernate.Action.EntityUpdateAction.Execute()
在 NHibernate.Engine.ActionQueue.Execute(IExecutable 可执行文件)
在 NHibernate.Engine.ActionQueue .ExecuteActions(IList 列表)
at NHibernate.Engine.ActionQueue.ExecuteActions()
at NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions(IEventSource session)
我曾经认为 NHibernate 没有黑魔法,但这让我想拜访我的巫毒牧师,向其他人寻求帮助领域...
有人知道 StaleStateException 来自哪里吗?这次我哪里做错了?
预先感谢
塞比
I'm having trouble with some NHibernate Code.Basically I'm trying to keep session lifetime as short as possible to minimize state information in the application.
I find it rather hard to describe my problem without being specific, so I'll just go ahead and use Article and Category metaphors.
public class Article
{
public virtual long Id { get; set; }
public virtual string Name { get; set; }
public virtual Category Category { get; set; }
}
public class Category
{
public virtual long Id { get; set; }
public virtual string Name { get; set; }
public override string ToString() { return "cat-" + this.Id.ToString(); }
}
public class ArticleMapping : ClassMap<Article>
{
public ArticleMapping()
{
this.Id( x => x.Id, "id" ).GeneratedBy.Assigned();
this.Map( x => x.Name, "name" );
this.References( x => x.Category ).Fetch.Join().Cascade.None();
}
}
public class CategoryMapping : ClassMap<Category>
{
public CategoryMapping()
{
this.ReadOnly();
this.Id(x => x.Id, "id").GeneratedBy.Assigned();
this.Map(x => x.Name, "name");
}
}
What I do is create a new Article, give it a name, Assign a Category from a list loaded via the method described below and attempt to save.
I get the following warning:
Unable to determine if cat-1 with assigned identifier is transient or detached
If you take a look at the model and mapping code, you will see that cat-1 should not be persisted at all - it is a Category and defined readonly and non-cascading.
//code that loads the list of categories
IStatelessSession session = this.SessionService.GetStatelessSession();
IList<Category> cats = session.CreateCriteria<Category>().List<Category>();
this.SessionService.EndSession(session);
//code that's called to save the instance
ISession session = this.SessionService.GetSession();
using (ITransaction transaction = session.BeginTransaction())
{
session.SaveOrUpdate(article);
transaction.Commit();
}
this.SessionService.EndSession(session);
If I call the Save method again, it all goes banana-shaped:
Exception:
NHibernate.StaleStateException: Unexpected row count: 0; expected: 1
Stacktrace:
at NHibernate.AdoNet.Expectations.BasicExpectation.VerifyOutcomeNonBatched(Int32 rowCount, IDbCommand statement)
at NHibernate.AdoNet.NonBatchingBatcher.AddToBatch(IExpectation expectation)
at NHibernate.Persister.Entity.AbstractEntityPersister.Update(Object id, Object[] fields,
Object[] oldFields, Object rowId, Boolean[] includeProperty, Int32 j, Object oldVersion, Object obj, SqlCommandInfo sql, ISessionImplementor session)
at NHibernate.Persister.Entity.AbstractEntityPersister.UpdateOrInsert(Object id, Object[] fields, Object[] oldFields, Object rowId, Boolean[] includeProperty, Int32 j, Object oldVersion, Object obj, SqlCommandInfo sql, ISessionImplementor session)
at NHibernate.Persister.Entity.AbstractEntityPersister.Update(Object id, Object[] fields, Int32[] dirtyFieldA first chance exception of type 'NHibernate.StaleStateException' occurred in NHibernate.dll
s, Boolean hasDirtyCollection, Object[] oldFields, Object oldVersion, Object obj, Object rowId, ISessionImplementor session)
at NHibernate.Action.EntityUpdateAction.Execute()
at NHibernate.Engine.ActionQueue.Execute(IExecutable executable)
at NHibernate.Engine.ActionQueue.ExecuteActions(IList list)
at NHibernate.Engine.ActionQueue.ExecuteActions()
at NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions(IEventSource session)
I used to think NHibernate was free from black magic, but this makes me wanna visit my voodoo priest to ask for help from the other realm...
Does anyone have any idea where the StaleStateException is coming from? Where did I go wrong this time around?
Thanks in advance
Sebi
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
不确定,但问题可能是您使用一个会话加载对象,并使用另一个会话更新它。
在这种情况下,该对象确实是分离的。
尝试使用 Bind() 函数将对象绑定到第二个会话,或者考虑仅使用一个会话(请注意,会话 != 数据库连接。含义 - 打开一个会话应该不会太昂贵,而且不是一个坏主意) 。
not sure, but the problem might be the fact that you load the object using one session, and update it using another.
in that scenario, the object is, indeed- detached.
try using the Bind() function to bind the object to the second session, or consider using just one session (note that session != db connection. meaning- having a session open should not be too expensive, and is not a bad idea).
哎呀!有时,在向世界大声喊出之前检查所有本地内容会有所帮助...某个地方有一个数据库触发器覆盖了 NHibernate 从序列中获取的 ID。
含义:
该警告是完全可以理解的,
作为分配给的类别
文章没有会话状态,因此
必须通过 fetch 或
或者 session.Refresh(),
其结果相同。
StaleObjectStateException 必须
发生,因为该行是
持续存在,无法位于
表(谢谢,触发器先生!)
抱歉打扰你们了……
Yuck! Sometimes it helps to check all local stuff before shouting it out to into the world...There was a Database trigger somewhere that overwrote the ID NHibernate had fetched from the sequence.
Meaning :
The warning was fully comprehensible,
as the category assigned to the
article had no session state, so that
hasto be determined via fetch or
alternatively session.Refresh(),
which has the same result.
The StaleObjectStateException had to
occur, since the row, being
persistent, could not be located in
the table (thanks, Mr. Trigger!)
Sorry for bothering y'all...