【问题标题】:Many to Many delete in NHibernate two parents with common association多对多在 NHibernate 中删除两个具有共同关联的父母
【发布时间】:2010-12-26 01:46:02
【问题描述】:

我的应用中有 3 个顶级实体:电路、问题、文档

Circuits 可以包含 Documents,Issue 可以包含 Documents。

当我删除一个电路时,我希望它删除与之关联的文档,除非它被其他人使用。我希望对问题有同样的行为。当唯一的关联在数据库中的同一个表中时,我让它工作,但如果它在另一个表中,那么它会由于外键约束而失败。

ex 1(这将正确级联,因为从电路到文档只有一个外部约束
Document1 存在。
Circuit1 存在并包含对 Document1 的引用。
如果我删除 Circuit1,那么它会同时删除 Document1。

ex 2(这将正确级联,因为从电路到文档只有一个外部约束。
Document1 存在。
Circuit1 存在并包含对 Document1 的引用。
Circuit2 存在并包含对 Document1 的引用。
如果我删除 Circuit1,那么它会被删除,但 Document1 不会被删除,因为 Circuit2 存在。
如果我随后删除 Circuit2,则 Document1 将被删除。

ex 3(这将引发错误,因为当它删除电路时,它会看到没有其他电路引用该文档,因此它会尝试删除该文档。但是它不应该,因为有一个对文档有外部约束的问题。)
文档 1 存在。
Circuit1 存在并包含对 Document1 的引用。
问题 1 存在并包含对 Document1 的引用。
如果我删除 Circuit1,那么它会失败,因为它会尝试删除 Document1,但 Issues1 仍然有参考。

数据库:
这个想法不会让上传图片,所以这里是数据库的 ERD:http://lh3.ggpht.com/_jZWhe7NXay8/TROJhOd7qlI/AAAAAAAAAGU/rkni3oEANvc/CircuitIssues.gif

型号:

public class Circuit
{
    public virtual int CircuitID { get; set; }
    public virtual string CJON { get; set; }
    public virtual IList<Document> Documents { get; set; }
}
public class Issue
{
    public virtual int IssueID { get; set; }
    public virtual string Summary { get; set; }
    public virtual IList<Model.Document> Documents { get; set; }
}
public class Document
{
    public virtual int DocumentID { get; set; }
    public virtual string Data { get; set; }
}

映射文件:

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Model" assembly="Model">
  <class name="Circuit" table="Circuit">
    <id name="CircuitID">
      <column name="CircuitID" not-null="true"/>
      <generator class="identity" />
    </id>
    <property name="CJON" column="CJON" type="string" not-null="true"/>
    <bag name="Documents" table="CircuitDocument" cascade="save-update,delete-orphan">
      <key column="CircuitID"/>
      <many-to-many class="Document">
        <column name="DocumentID" not-null="true"/>
      </many-to-many>
    </bag>
  </class>
</hibernate-mapping>
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Model" assembly="Model">
  <class name="Issue" table="Issue">
    <id name="IssueID">
      <column name="IssueID" not-null="true"/>
      <generator class="identity" />
    </id>
    <property name="Summary" column="Summary" type="string" not-null="true"/>
    <bag name="Documents" table="IssueDocument" cascade="save-update,delete-orphan">
      <key column="IssueID"/>
      <many-to-many class="Document">
        <column name="DocumentID" not-null="true"/>
      </many-to-many>
    </bag>
  </class>
</hibernate-mapping>
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Model" assembly="Model">
  <class name="Document" table="Document">
    <id name="DocumentID">
      <column name="DocumentID" not-null="true"/>
      <generator class="identity" />
    </id>
    <property name="Data" column="Data" type="string" not-null="true"/>
  </class>
</hibernate-mapping>

代码:

using (ISession session = sessionFactory.OpenSession())
{
    var doc = new Model.Document() { Data = "Doc" };

    var circuit = new Model.Circuit() { CJON = "circ" };
    circuit.Documents = new List<Model.Document>(new Model.Document[] { doc });

    var issue = new Model.Issue() { Summary = "iss" };
    issue.Documents = new List<Model.Document>(new Model.Document[] { doc });

    session.Save(circuit);
    session.Save(issue);
    session.Flush();                
}
using (ISession session = sessionFactory.OpenSession())
{
    foreach (var item in session.CreateCriteria<Model.Circuit>().List<Model.Circuit>())
    {
        session.Delete(item);
    }
    //this flush fails, because there is a reference to a child document from issue
    session.Flush();
    foreach (var item in session.CreateCriteria<Model.Issue>().List<Model.Issue>())
    {
        session.Delete(item);
    }
    session.Flush();
}

【问题讨论】:

    标签: c# nhibernate many-to-many cascade


    【解决方案1】:

    您需要在删除父级之前清除集合。 delete-orphan 导致删除操作级联到子级,因为子级仍在集合中被引用。在解除引用之前,该对象没有成为孤儿的可能性,因此不会对其进行检查。 Section 10.11 in this documentation 解释得很好,但仍然令人困惑。据我了解,delete-orphan 在删除父级时有两种可能的结果:

    1. 如果父集合中仍引用子对象,则将其删除。
    2. 如果子项在父项的集合中不再被引用,则如果它是孤儿,即没有其他引用,则将其删除。

    我认为这会如愿以偿:

        using (ISession session = sessionFactory.OpenSession())
        {
            foreach (var item in session.CreateCriteria<Model.Circuit>().List<Model.Circuit>())
            {
                item.Documents.Clear();
                session.Delete(item);
            }
            session.Flush();
            foreach (var item in session.CreateCriteria<Model.Issue>().List<Model.Issue>())
            {
                item.Documents.Clear();
                session.Delete(item);
            }
            session.Flush();
        }
    

    【讨论】:

    • 添加的行:item.Documents.Clear();实际上不会导致行为上的差异。问题不在于在处理单个实体时这些行没有被正确删除。问题是与文档实体有关系的第二个实体仍然持有引用。该建议与我提供的示例存在相同的问题。
    • 第二个实体有什么问题 - 从您的问题中不清楚。
    • 使用 DB 模型作为参考:lh3.ggpht.com/_jZWhe7NXay8/TROJhOd7qlI/AAAAAAAAAGU/rkni3oEANvc/… Assume: DocumentID: 1 IssueID: 1
    • 问题在于 Nhibernate 在确定什么是 orhpan 和什么不是时似乎只查看一个关系。我试图在原始帖子中添加更多细节。这是否使它更清楚?我是该网站的新手,我正在努力提供尽可能多的细节,并将提供任何需要明确的信息。谢谢!
    • 所以在这一点上,我的研究表明这只是 Nhibernate 的一个限制。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-29
    • 2011-05-06
    相关资源
    最近更新 更多