我在两个对象(many-to-many
,Application
)之间存在Query
关系。我构建了地图,在两个对象图中都有一个HasManyToMany映射。
我第一次使用SaveOrUpdate应用程序时,它工作正常,并且条目正确地放在连接表中。
但是,第二次使用它时,我收到错误:'具有相同标识符的另一个对象已经与会话关联'。
在下面显示的addQuery代码的最后几行中抛出异常。
这是实体和地图。任何建议都将不胜感激。
public class Application
{
public virtual int id { get; set; }
public virtual string name { get; set; }
public virtual IList<Query> queries { get; set; }
public Application()
{
this.queries = new List<Query>();
}
public virtual void AddQuery(Query qry)
{
qry.applicationsUsedIn.Add(this);
queries.Add(qry);
}
}
public class Query
{
public virtual int id { get; protected set; }
public virtual string name { get; set; }
public virtual string query { get; set; }
public virtual IList<QueryParameters> parameters { get; set; }
public virtual IList<Application> applicationsUsedIn { get; set; }
public Query()
{
this.parameters = new List<QueryParameters>();
this.applicationsUsedIn = new List<Application>();
}
public virtual void AddParameter(QueryParameters qp)
{
qp.query = this;
this.parameters.Add(qp);
}
}
public class ApplicationMap : ClassMap<Application>
{
public ApplicationMap()
{
Table("dbo.Applications");
Id(x => x.id).Column("id");
Map(x => x.name).Column("name");
HasManyToMany(x => x.queries)
.Table("dbo.ApplicationsQueries")
.ParentKeyColumn("appid")
.ChildKeyColumn("qryid")
.Not.LazyLoad()
.Cascade.SaveUpdate();
}
}
public class QueryMap : ClassMap<Query>
{
public QueryMap()
{
Table("dbo.Queries");
Id(x => x.id);
Map(x => x.name);
Map(x => x.query);
HasMany(x => x.parameters)
.Cascade.All()
.Inverse();
HasManyToMany(x => x.applicationsUsedIn)
.Table("dbo.ApplicationsQueries")
.ParentKeyColumn("qryid")
.ChildKeyColumn("appid")
.Inverse()
.Cascade.SaveUpdate()
.Not.LazyLoad();
}
}
public void addQuery(string appname, string qryname, string qrystr)
{
Application app = getApplication(appname);
if (null == app)
{
app = addApplication(appname);
}
Query qry = getQuery(appname, qryname);
if (null == qry)
{
using (ISessionFactory isf = getSessionFactory())
{
using (var sess = isf.OpenSession())
{
using (var tran = sess.Transaction)
{
tran.Begin();
qry = new Query();
qry.name = qryname;
qry.query = qrystr;
sess.Save(qry);
tran.Commit();
}
}
}
}
if (!app.queries.Contains(qry))
{
using (ISessionFactory isf = getSessionFactory())
{
using (var sess = isf.OpenSession())
{
using (var tran = sess.Transaction)
{
tran.Begin();
app.AddQuery(qry);
//This is where the exception is thrown
sess.SaveOrUpdate(app);
tran.Commit();
}
}
}
}
}
public ApplicationMap()
{
Table("dbo.Applications");
Id(x => x.id).Column("id");
Map(x => x.name).Column("name");
HasManyToMany(x => x.queries)
.Table("dbo.ApplicationsQueries")
.ParentKeyColumn("appid")
.ChildKeyColumn("qryid")
.LazyLoad();
}
public QueryMap()
{
Table("dbo.Queries");
Id(x => x.id);
Map(x => x.name);
Map(x => x.query);
HasMany(x => x.parameters)
.Cascade.All()
.Inverse();
HasManyToMany(x => x.applicationsUsedIn)
.Table("dbo.ApplicationsQueries")
.ParentKeyColumn("qryid")
.ChildKeyColumn("appid")
.Inverse()
.LazyLoad();
}
public void addQuery(string appname, string qryname, string qrystr)
{
using (ISessionFactory isf = getSessionFactory())
{
using (var sess = isf.OpenSession())
{
using (var tran = sess.Transaction)
{
tran.Begin();
var critapp = sess.CreateCriteria<Application>()
.Add(Restrictions.Eq("name", appname));
Application app = (Application)critapp.UniqueResult();
if (null == app)
{
app = new Application();
app.name = appname;
sess.Save(app);
}
var critqry = sess.CreateCriteria<Query>()
.Add(Restrictions.Eq("name", qryname));
Query qry = (Query)critqry.UniqueResult();
if (null == qry)
{
qry = new Query();
qry.name = qryname;
qry.query = qrystr;
sess.Save(qry);
}
if (!app.queries.Contains(qry))
{
app.AddQuery(qry);
}
tran.Commit();
}
}
}
}
答案 0 :(得分:0)
此处的问题隐藏在(实际上)一个操作的许多已打开会话中。这不是执行此插入/更新的正确方法。我们应该始终将一组依赖于彼此的操作包装成一个会话,一个交易。
所以发生的事情就是在这里我们得到了Query
这样的回复:
Query qry = getQuery(appname, qryname);
我们有一个对象,它是(was)会话的一部分,它刚刚用完了作用域。 qry
实例现已完全填充,因为
IList<Application> applicationsUsedIn
的映射(请参阅您的映射)为.Not.LazyLoad()
Not.LazyLoad()
映射也是如此...... 所以一旦我们来到最后一个事务(以及它自己的会话)......我们的对象可以被深度填充......与上一个会话中的加载对象具有相同的ID
快速解决问题:
Save()
,如果我们有新对象getQuery()
检索的对象,getApplication()
(在同一会话中)不要调用SaveOrUpdate()
。他们已经在会话中,这就是本案例中SaveOrUpdated主要做的事情(将它们放入会话中)注意:我会更改多对多
的映射