流畅的NHibernate只能级联删除关联记录

时间:2012-12-06 20:47:51

标签: nhibernate fluent-nhibernate

我们正在使用NHibernate作为会员系统。 User可以是许多Role的成员,而Role可以包含许多用户。

当删除RoleUser时,它应该只删除关联记录的删除(“RoleUsers”表)。

删除Role按预期工作。但是,删除User不会删除关联记录,因此会因外键约束而失败。

我在Role方面的映射:

        HasManyToMany(r => r.Users)
            .Access.CamelCaseField()
            .Table("RoleUsers")
            .ParentKeyColumn("RoleId")
            .ChildKeyColumn("UserId")
            .AsSet();

User方面的映射:

        HasManyToMany(u => u.Roles)
            .Access.CamelCaseField()
            .Table("RoleUsers")
            .ParentKeyColumn("UserId")
            .ChildKeyColumn("RoleId")
            .Inverse(); // we'll add user to role, not role to user

失败的测试:

    [Test]
    public void Deleting_user_should_not_delete_roles()
    {
        var user = new User("john@doe.com", "John", "Doe", "Secr3t");
        var role = new Role("Admin");
        role.AddUser(user);

        object id;
        using (var txn = Session.BeginTransaction())
        {
            id = Session.Save(user);
            Session.Save(role);
            txn.Commit();
        }

        Session.Clear();

        var fromDb = Session.Get<User>(id);

        using (var txn = Session.BeginTransaction())
        {
            Session.Delete(fromDb);
            txn.Commit();
        }

        Session.Query<Role>().Count().ShouldEqual(1);
    }

我已尝试用户映射的Cascade的每个组合,它会失败或删除关联记录和角色(不是我想要的)。

1 个答案:

答案 0 :(得分:2)

反向级联是两个不同的概念。当然,<many-to-many>关系支持 。请参阅文档6.8(向下向下滚动到6.9)

http://nhibernate.info/doc/nh/en/index.html#collections-bidirectional

1)首先,我们可以删除User.Roles集合的反向设置。这会将用户的关系的行为拉直到角色,并在用户本身被删除之前强行删除。

2)其次。如果用户角色集合标记为 inverse

HasManyToMany(u => u.Roles)
  ...
  .Inverse(); // we'll add user to role, not role to user

删除任何用户永远不会触发删除该对。那是因为我们明确地说:唯一一个关心这种关系的人是Role

因此,如果我们想继续您的方案:

  

.Inverse(); //我们会将用户添加到角色,而不是将角色添加到用户

我们应该是有益的。 “我们将从角色中移除用户,而不是来自用户的角色”:

[Test]
public void Deleting_user_should_not_delete_roles()
{
  var user = new User("john@doe.com", "John", "Doe", "Secr3t");
  var role = new Role("Admin");
  role.AddUser(user);

  object roleId;
  object id;
  using (var txn = Session.BeginTransaction())
  {
    id = Session.Save(user);
    roleId = Session.Save(role);
    txn.Commit();
  }

  Session.Clear();

  // take both from DB
  var userFromDb = Session.Get<User>(id);
  var roleFromDb = Session.Get<Role>(roleId);

  using (var txn = Session.BeginTransaction())
  {
     // HERE just remove the user from collection
     roleFromDb.Users.Remove(userFromDb);

     // all relations will be deleted
     Session.Save(roleFromDb);
     txn.Commit();
  }
  ... 
  // assertions
  // "John's" relation to Role "admin" is deleted
}

注: 3)那里没有使用Cascade,但可以帮助减少Session.Save(用户)......

编辑:扩展第3点

删除用户,正如Ben Foster在评论中注意到的那样。

3)我们甚至应该允许Role完全管理其 Users 集合。让我们介绍casdace="all" (如果没有任何角色的用户应该删除,则为casdace =“all-delete-orhpan”。现在我们只能通过Role对象添加/更新用户。

Role的Users集合的映射应如下所示:

HasManyToMany(r => r.Users)
  .Access.CamelCaseField()
  .Table("RoleUsers")
  .ParentKeyColumn("RoleId")
  .ChildKeyColumn("UserId")
  //.Cascade.All(); // just save or update instance of users
  .Cascade.AllDeleteOrphan(); // will even delete User without any Role
  .AsSet();

有反向和级联我们可以调整测试:

[Test]
public void Deleting_user_should_not_delete_roles()
{
  var user = new User("john@doe.com", "John", "Doe", "Secr3t");
  var role = new Role("Admin");
  role.AddUser(user);

  object roleId;
  using (var txn = Session.BeginTransaction())
  {
     // I. no need to save user
     roleId = Session.Save(role);
     ...

后来称之为摆脱用户

...
using (var txn = Session.BeginTransaction())
{
  var user = Session.Get<User>(id);
  var roles = user.Roles.ToList();
  roles.ForEach(role => role.RemoveUser(user))
  // II. not only relations, but even the User is deleted
  // becuase updated roles triggered delete orhpan
  // (no Session.Update() call there)
  txn.Commit();
}