tags:

views:

23

answers:

2

I have a simple NHibernate domain model with Users and Roles. There's a many to many association between User and Roles. Simplified entities:

public class User : Entity<Guid>
{
    private IList<Role> _roles;

    public User()
    {
        _roles = new List<Role>();
    }

    public virtual UserName { get; set; }

    public virtual IList<Role> Roles { get; protected set; }
}

public class Role : Entity<Guid>
{
    private IList<User> _users;

    public Role ()
    {
        _users = new List<User>();
    }

    public virtual RoleName { get; set; }

    public virtual IList<User> Users { get; protected set; }
}

The base class has a property for Id.

The mappings are this:

public class UserMap : ClassMap<User>
{
    public UserMap()
    {
        LazyLoad();
        Id(x => x.Id)
            .GeneratedBy
            .GuidComb();

        Map(x => x.UserName)
            .Length(50)
            .Not.Nullable()
            .Unique();

        HasManyToMany(x => x.Roles)
            .Cascade.SaveUpdate();
    }
}

public class RoleMap : ClassMap<Role>
{
    public RoleMap()
    {
        LazyLoad();
        Id(x => x.Id)
            .GeneratedBy.GuidComb();

        Map(x => x.RoleName)
            .Length(20)
            .Unique()
            .Not.Nullable();

        HasManyToMany(x => x.Users)
        .Cascade.SaveUpdate()
        .Inverse();
    }
}

I let FluentNH create my DDL and can create/update/delete users and roles. The problem is in my unit test for can_add_role_to_user(). The simplified test is as follows:

public void can_add_role_to_user ()
{
    var user = members.CreateUser(username);
    var role = members.CreateRole(rolename, roledescription);

    bool result = members.AddUserToRole (username, rolename);

    // Assert
    Assert.AreEqual (true, result);
    var savedUser = members.GetUserByName(username);
    Assert.IsNotNull(savedUser);
    Assert.AreEqual (1, savedUser.Roles.Count); // Check role has been added to user
    Assert.AreEqual (user, savedUser);
    Assert.AreEqual (role, savedUser.Roles[0]);

    var savedRole = members.GetRoleByName(rolename);
    Assert.IsNotNull(newrole);
    Assert.AreEqual(1, newrole.Users.Count);    // Check user has been added to role - fails
    Assert.AreEqual (role, newrole);
    Assert.AreEqual (user, newrole.Users[0]);
}

The first set of Asserts pass - the IList in User has one entry. However the IList in Role has zero entries. I know that I can only save from the User entity as I have Inverse on the collection map, but I would expect NH to make sure both sides of the collection were hydrated correctly or am I missing something.

+1  A: 

Where is the session flushed and cleared? Are you clearing you nhibernate session between your AddUserToRole method and your asserts?

Could be that members.GetRoleByName(rolename) is fetching the role from the identity map.

Torkel
The AddUserToRole method has the Get<User> and SaveOrUpdate<User> within a Transaction. There is no implicit flush or clear of the session before the Assert.
AndyB
I changed the Asserts slightly and put Assert.AreEqual (role, newRole) and Assert.IsSame(role, newRole) before the Assert(1, newRole.Users.Count()) and the AreEqual and IsSame assertions pass, but Count does not. I'm thinking that the GetRoleByName is fetching from the identity map since they're AreEqual and IsSame. I'm thinking I need a flush.
AndyB
A: 

I've worked out what's going on with the bi-directional relationship. When the Role is added to the Roles collection of User NH doesn't make the other side of the relationship. The easiest way to force NH to make the bi-directional is to either Clear the ISession or to Evict the two entities (my preference). This forces NH to access the database and correctly construct both sides of the relationship. i.e. newUser.Roles[0] contains the added role and newRole.Users[0] contains the user - just as I wanted.

I also found that my test harness had a bug and was creating two different Sessions :-(

AndyB