流利的NHibernate:仅在多对多关系中从一侧进行级联删除

时间:2010-02-10 23:11:05

标签: c# fluent-nhibernate

我正在尝试编写正确的地图配置,这样我就可以在多对多关系设置中仅从一侧删除。

下面是我的Map和Entity类的代码结构以及实际程序(伪)代码和SQL模式。相当简单直接。

我们有一个人员表和一个文件表。然后我们有一个personfile表,因为一个人可以有很多文件,同样,一个文件可以分配给很多人。

现在,当我删除个人记录时,将删除personfile中的相关记录和属于该人的文件。到目前为止一切都很好。

如果我删除了一个文件(如下面的Program.cs所示),我希望它从人文件和文件中删除,但不是从人那里删除。

然而,按照我设置的方式,NHibernate只调用文件表上的delete,这会导致错误。例如

ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`test`.`personfile`, CONSTRAINT  `FK_PersonFile2` FOREIGN KEY (`FileID`) REFERENCES `file` (`FileID`))

我确实将Cascade.Delete()添加到了FileMap,但是当我这样做时,从文件表中删除也会从人员表中删除。

重申一下,我最终想要的是调用Delete(文件),而delete(文件)又会删除personfile表和文件表中的记录,但不会删除人员表。

我是否应该继续获取人员记录的路线,然后从person.Files []集合中删除文件记录,然后调用SaveOrUpdate()?

考虑一下情景。

首先确保我们在所有表格中都有良好的数据。

mysql> SELECT f.FileID, p.PersonID, p.Name, f.Filename
    -> FROM personfile pf
    ->   LEFT OUTER JOIN file f on pf.FileID = f.FileID
    ->   LEFT OUTER JOIN person p on pf.PersonID = p.PersonID
    -> ;
+--------+----------+------+-------------+
| FileID | PersonID | Name | Filename    |
+--------+----------+------+-------------+
|      1 |        1 | John | Apples.jpg  |
|      2 |        1 | John | Oranges.jpg |
|      3 |        2 | Bob  | Grapes.jpg  |
+--------+----------+------+-------------+
3 rows in set (0.00 sec)

现在处于正常情况下,如果你试图仅删除文件(这是NHibernate根据我的设置尝试做的事情),这就是预期会发生的事情。

mysql> DELETE FROM file WHERE file.FileID = 2;
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`test`.`pe
rsonfile`, CONSTRAINT `FK_PersonFile2` FOREIGN KEY (`FileID`) REFERENCES `file` (`FileID`))

我希望NHibernate做的是这样的事情,首先删除关系表中的记录,然后删除atual文件表中的记录。 只要最终结果相同,查询就不必具体。

mysql> DELETE pf, f
    -> FROM personfile pf
    ->   LEFT OUTER JOIN file f on pf.FileID = f.FileID
    ->   LEFT OUTER JOIN person p on pf.PersonID = p.PersonID
    -> WHERE pf.FileID = 2
    -> ;
Query OK, 2 rows affected (0.05 sec)

上述删除的结果有效。

mysql> SELECT f.FileID, p.PersonID, p.Name, f.Filename
    -> FROM personfile pf
    ->   LEFT OUTER JOIN file f on pf.FileID = f.FileID
    ->   LEFT OUTER JOIN person p on pf.PersonID = p.PersonID
    -> ;
+--------+----------+------+------------+
| FileID | PersonID | Name | Filename   |
+--------+----------+------+------------+
|      1 |        1 | John | Apples.jpg |
|      3 |        2 | Bob  | Grapes.jpg |
+--------+----------+------+------------+
2 rows in set (0.00 sec)

Program.cs的

File file = db.Session.Load<File>(2);

session.Delete(file);

transaction.Commit();

映射

public class FileMap : ClassMap<File>
{
  public FileMap()
  {
    Id(x => x.FileID)
      .GeneratedBy.Identity();
    Map(x => x.Filename)

    HasManyToMany(x => x.Persons)
      .Table("PersonFile")
      .Inverse()
      .ParentKeyColumn("FileID")
      .ChildKeyColumn("PersonID");
  }
}

public class PersonMap : ClassMap<Person>
{
  public PersonMap()
  {
    Id(x => x.PersonID)
      .GeneratedBy.Identity();
    Map(x => x.Name)

    HasManyToMany(x => x.Files)
      .Table("PersonFile")
      .Cascade.Delete()
      .ParentKeyColumn("PersonID")
      .ChildKeyColumn("FileID");
  }
}

实体

public class File
{
  public virtual uint FileID { get; set; }
  public virtual string Filename { get; set; }

  public virtual IList<Person> Persons { get; set; }
}

public class Person
{
  public virtual uint PersonID { get; private set; }
  public virtual string Name { get; set; }

  public virtual IList<File> Files { get; set; }
}

SQL

CREATE TABLE `file` (
  `FileID` int(11) unsigned NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`FileID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

CREATE TABLE `person` (
  `PersonID` int(11) unsigned NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`PersonID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

CREATE TABLE `personfile` (
  `PersonID` int(11) unsigned NOT NULL DEFAULT '0',
  `FileID` int(11) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (`PersonID`,`FileID`),
  KEY `FK_PersonFile2` (`FileID`),
  CONSTRAINT `FK_PersonFile1` FOREIGN KEY (`PersonID`) REFERENCES `person` (`PersonID`),
  CONSTRAINT `FK_PersonFile2` FOREIGN KEY (`FileID`) REFERENCES `file` (`FileID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

2 个答案:

答案 0 :(得分:2)

尝试调用File.Persons.Clear()从文件中删除链接的人员,然后再将其删除。

我对这个要求感到好奇;第二句似乎与它相矛盾,因为您可以删除链接到与您要删除的人不同的人的文件记录。

  

我们有一个人员表和一个文件表。然后我们有一个personfile表   因为一个人可以有很多文件和   同样,可以分配文件   很多人。

     

现在,当我删除一个人的记录时,   personfile和中的相关记录   属于该人的文件   删除。到目前为止一切都很好。

答案 1 :(得分:0)

File.Persons.Clear();

Session.Flush();

Session.CreateQuery("delete from File").ExecuteUpdate();

适合我!