fluentnhibernate有许多相同的标识符异常



我在两个对象(ApplicationQuery)之间有一个many-to-many关系。我已经构建了在两个对象映射中都具有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();
}
}
}
}

这里的问题隐藏在许多为打开的会话中(事实上)一个操作。这不是执行此插入/更新的正确方式。我们应该始终将一组相互依赖的操作包装成一个会话单个事务

所以发生的事情是,在这里,我们收到了这样的Query

Query qry = getQuery(appname, qryname);

我们有一个对象,它是会话的(曾经)部分,刚刚超出范围。qry实例现在已完全填充,因为

  • 它是一个现有的对象(从DB加载)
  • 集合IList<Application> applicationsUsedIn的映射(请参阅映射)为.Not.LazyLoad()
  • 对于其他CCD_ 8映射也是如此

因此,一旦我们进入最后一个事务(以及它自己的会话)。。。我们的物体可能会被深深地占据。。。具有与上次会话内加载的对象相同的ID

快速解决问题:

  1. 在操作开始时打开会话
  2. 在开始时打开交易
  3. 如果确实有新对象,则仅调用Save()
  4. 对于通过getQuery()检索的对象,getApplication()(在同一会话中)不要调用SaveOrUpdate()。它们已经在会话中,这就是SaveOrUpdated在本例中的主要作用(将它们放入会话中)
  5. a) 调用交易。Commit(),所有内容都将正确持久化
    b)在操作结束时关闭会话

注意:我会更改您的多对多的映射

  • 删除Not.LazyLoad()。懒惰是我们最想要的
  • 删除Cascade,因为它与另一端有关,而与配对表无关如果这是故意的,那就别提了

相关内容

最新更新