0
votes

I have 2 tables with manyToMany relationship. user can have many groups and groups can have many users.

public class User
{
   public virtual Guid Id { get; set; }
   public virtual UInt64 UserId { get; set; }
   public virtual IList<Group> Groups{ get; set; }
}

public class Group
{
   public virtual Guid Id { get; set; }
   public virtual Guid GroupGuid  { get; set; }
}

I use Fluent NHibernate to AutoMap them as follows:

.Override<User>(obj => obj.HasManyToMany(x => x.Groups).Table("UserToGroup").Cascade.AllDeleteOrphan())

As a result Nhibernate produce 3 tables: 1. Users - Mapped object table 2. Groups Mapped Object table 3. UserToGroup - generated association table

The requirement is to have the following functionality on these tables: 1. AddUser(UInt64 UserId, IEnumerable groups) 2. RemoveGroup(GroupGuid groupGuild) 3. Dictionary> GetGroupToUsers();

AddUser method is work Ok, All table updated OK: * Insert of new user in User table. OK * Insert of new group in Group table. OK * Insert of new relations recods in UserToGroup table. OK

I use the following code to add a User (including groups):

public void AddUser(User userData, IList<Guid> groupIds)
{
   User user = new User();
   user.UserId = userData.UserId;

   IList<User> Users = new List<Users();
   Users.Add(user);

   IList<Group> groups = new List<Group>();

   foreach (Guid groupId in groupIds)
   {
      Group grp = new Group();
      grp.GroupGuid = groupId;
      grp.Users = Users;
      groups.Add(grp);
   }

   user.Groups = groups;
   Session.Save(user);
}

I use the following code to Remove group:

public void RemoveGroup(GroupGuid groupGuild)
{
     IList<Group> groupsToRemove = Session.QueryOver<Group>().Where(
            row => row.GroupGuid == groupGuild).List();
     foreach (Group group in groupsToRemove)
     {
          group.Users.Clear();
          Session.Delete(group);
     }
}

RemoveGroup method failed with the following exception:

threw exception: 
NHibernate.Exceptions.GenericADOException: could not delete: [StorageObject.Group#11111111-1111-1111-1111-111111111111][SQL: DELETE FROM "Group" WHERE Id = ?] ---> System.Data.SqlServerCe.SqlCeException: The primary key value cannot be deleted because references to this key still exist. [ Foreign key constraint name = FK4B29424018FA0CD4 ]

I understand that Group table can't be delete since the reference UserToGroup table point to Group table.

  1. 1st question: how can I make hibernate to understand that I want to be able to controller this manyToMany relation from both side: Add users with groups - from user side. And delete groups - from group side

I tried to move Inverse to the User table but in this case when I add a User the association table is not populate.

What is the best way to define this kind of manyToMany relation?

Update: Another way I tried is to have List of Users also in Groups in this configuration removeGroup is wokring but addUser in not working for specific scenario:

public class Group
{
   public virtual Guid Id { get; set; }
   public virtual Guid GroupGuid  { get; set; }
   public virtual IList<User> Users{ get; set; }
}

public class User
{
   public virtual Guid Id { get; set; }
   public virtual UInt64 UserId { get; set; }
   public virtual IList<Group> Groups{ get; set; }
}

Now I have a bidirectional many to many. My Fluent mapping is:

.Override<User>(obj => obj.HasManyToMany(x => x.Groups).ParentKeyColumn("UserId").ChildKeyColumn("GroupId").Table("UserToGroup").Cascade.SaveUpdate()
.Override<Group>(obj => obj.HasManyToMany(x => x.Users).ParentKeyColumn("GroupId").ChildKeyColumn("UserId").Table("UserToGroup")

If I use this configuration RemoveGroup works fine but AddUser is not working for the following case: When adding 2 Users which contains the same Group the association table delete the first relation and holds just the last reference instead of having both relations. Please advise.

1

1 Answers

1
votes

Try to add an inverse collection in Group:

public class Group
{
    public virtual Guid Id { get; set; }
    public virtual Guid GroupGuid  { get; set; }
    public virtual IList<User> Users { get; set; }
}

In your mapping:

.Override<Group>(obj => obj.HasManyToMany(x => x.Users).Table("UserToGroup").Inverse().Cascade.SaveUpdate())

Your Remove method:

public void RemoveGroup(GroupGuid groupGuild)
{
     IList<Group> groupsToRemove = Session.QueryOver<Group>().Where(
            row => row.GroupGuid == groupGuild).List();
     foreach (Group group in groupsToRemove)
     {
          group.Users.Clear();   // Removes references from all users pointing to that group
          Session.Delete(group);
     }
}

When you commit the transaction, the references should be automatically removed. I never tried this by myself, but it might work for you.