Search code examples
c#nhibernatefluent-nhibernate

Mapping a User, Roles and RolePermissions


I have the following entities:

User
Role

and then a join table:

UsersRoles (UserId, RoleId)

and then a RolePermissions table:

RolePermissions(Id, RoleId, ....)

So on the User entity I want to be able to do:

user.Roles
user.RolePermissions

My UserMap looks like:

public class UserMap : ClassMap<User>
{
   public UserMap()
   {

      HasManyToMany(x => x.Roles)
            .Table("RolesUsers")
            .Access.CamelCaseField(Prefix.Underscore)
            .ParentKeyColumn("UserId")
            .ChildKeyColumn("RoleId")
            .Cascade.All()
            .Inverse();
   }

}

Now when I create a new user like:

var user = new User { ... };
user.AddRole(new Role { .... } );

UserRepository.Create(user);

It saves the user, and saves the role, but doesn't insert into the RolesUsers table?

Also how do I add a property to get all the RolePermissions for a User?

Update

My rolemap has:

 HasManyToMany(x => x.Users)
            .Table("RolesUsers")
            .Access.CamelCaseField(Prefix.Underscore)
            .ParentKeyColumn("RoleId")
            .ChildKeyColumn("UserId")
            .Cascade.All();

My unit test actually passes, but I think it is because I don't know how to purge the 1st level cache and it isn't even querying the database with the call for testUser:

     [Test]
    public void ShouldAddARoleToAUser()
    {
        int userId = -1;
        using (var tx = UserRepository.SessionFactory.OpenSession().BeginTransaction())
        {

            var user = FactoryGirl.GetUser();
            user.AddRole(FactoryGirl.GetRole());

            UserRepository.Create(user);

            tx.Commit();

            UserRepository.SessionFactory.OpenSession().Flush();

            userId = user.Id;
        }

        var testUser = UserRepository.Get(userId);

        Assert.IsNotNull(testUser);
        Assert.IsNotNull(testUser.Roles);
        Assert.IsTrue(testUser.Roles.Count == 1);
        Assert.IsTrue(testUser.Roles[0].Id > 0);

    }

Update II

My user.cs looks like:

        private IList<Role> _roles = new List<Role>(); 

        public virtual IList<Role> Roles
        { 
            get { return _roles;  }
        }

        public virtual void AddRole(Role role)
        {
            if(!_roles.Contains(role))
            {
                _roles.Add(role);
            }
        }

Role.cs:

        private IList<User> _users = new List<User>();
        public virtual IList<User> Users
        {
            get { return _users; }
        }

Update III

The strange this is my test passes, but looking at profiler I can see that it doesn't even hit sql server so even though I am calling Flush, and then doing a Get(userId) it is loading the entity from memory. Also, looking at the other end of the relation (Role), the Role.Users is {} so is that the issue?


Solution

  • If you are using private backing fields for your collections I think you have to map them like this:

    public class UserMap : ClassMap<User>
    {
       public UserMap()
       {
          HasManyToMany(x => x.Roles)
                    .Table("RolesUsers")
                    .Access.AsCamelCaseField(Prefix.Underscore)
                    .ParentKeyColumn("UserId")
                    .ChildKeyColumn("RoleId")
                    .Cascade.All()
                    .Inverse();
       }
    }
    

    Key difference above is .Access.AsCamelCaseField(Prefix.Underscore)

    Edit:

    In addition to this I think you need to simplify your test to something like the following:

    User newUser = new User();
    Role newRole = new Role();
    newUser.AddRole(newRole);
    newRole.AddUser(newUser);
    
    using (NHibernate.ISession session = iSessionFactory.OpenSession())
    {
        using (NHibernate.ITransaction tran = session.BeginTransaction())
        {
            session.Save(newUser);
            tran.Commit();
        }
    }
    

    The above may not be exact but I think you get the idea of what I'm talking about.