fluentnhibernate hasmanytomany相同的标识符异常

时间:2014-01-16 00:20:17

标签: nhibernate fluent-nhibernate fluent-nhibernate-mapping

我在两个对象(many-to-manyApplication)之间存在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();
                }
            }
        }
    }

1 个答案:

答案 0 :(得分:0)

此处的问题隐藏在(实际上)一个操作的许多已打开会话中。这不是执行此插入/更新的正确方法。我们应该始终将一组依赖于彼此的操作包装成一个会话一个交易

所以发生的事情就是在这里我们得到了Query这样的回复:

Query qry = getQuery(appname, qryname);

我们有一个对象,它是(was)会话的一部分,它刚刚用完了作用域。 qry实例现已完全填充,因为

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

所以一旦我们来到最后一个事务(以及它自己的会话)......我们的对象可以被深度填充......与上一个会话中的加载对象具有相同的ID

快速解决问题:

  1. 在操作开始时打开会话
  2. 在开头
  3. 打开交易
  4. 仅限Save(),如果我们有新对象
  5. 对于通过getQuery()检索的对象,getApplication()(在同一会话中)不要调用SaveOrUpdate()。他们已经在会话中,这就是本案例中SaveOrUpdated主要做的事情(将它们放入会话中)
  6. a)调用transaction.Commit(),所有内容都将被正确保留 b)在操作结束时关闭会话
  7. 注意:我会更改多对多

    的映射
    • 删除Not.LazyLoad()。懒惰是我们最想要的......
    • 删除Cascade,因为它是关于配对表的另一端。 如果是这样的话,那就留下