NHibernate provides the facility to map out many-to-many relationships without the need for the intersection table to be represented in the object model. Take for example, the following schema of Users, Roles and the intersection table that links users to many roles and roles to many users;
We then have the following object model to represent this;
or, in code if you prefer;
public class User { // Fields public virtual Guid Id { get; set; } public virtual string UserName { get; set; } public virtual string Email { get; set; } // Storage for the roles that this user belongs to private readonly IList<Role> _roles = new List<Role>(); // Accessor to list of roles - note we don't return list // that we can add/remove from - we use methods to do that // so we can implement any business logic. public virtual ReadOnlyCollection<Role> Roles { get { return new ReadOnlyCollection<Role>(_roles); } } // Method to associate a role with this user public virtual void AddRole( Role role ) { _roles.Add( role ); } // Method to remove a role from this user public virtual void RemoveRole( Role role ) { _roles.Remove(role); } }
public class Role { // Fields public virtual Guid Id { get; set; } public virtual string Name { get; set; } // Storage for the users that have this role private readonly IList<User> _users = new List<User>(); // Returns list of users belonging to this role // Notice this can't be added or removed from. // Nor is there any logic to add users into roles // as the link is maintained from the User object. public virtual ReadOnlyCollection<User> Users { get { return new ReadOnlyCollection<User>(_users); } } }
Notice that the relationship between users and roles is maintained and owned in the user object, not role. The role object just gives us information about which users belong to a role, but we can’t add or remove users into it without loading the user and accessing it’s Add/Remove Role methods. We can map this out as follows (notice, I’m using Fluent mappings for NHibernate here);
public class UserMap : ClassMap<User> { public UserMap() { base.Id(x => x.Id).GeneratedBy.GuidComb(); base.Map(x => x.UserName); base.Map(x => x.Email); HasManyToMany<Role>(Reveal.Property<User>("_roles")) .LazyLoad() .AsBag() .WithTableName("UserRoles") .WithParentKeyColumn("UserId") .WithChildKeyColumn("RoleId") .Access.AsReadOnlyPropertyThroughCamelCaseField(Prefix.Underscore) .Cascade.All(); } } public class RoleMap : ClassMap<Role> { public RoleMap() { base.Id(x => x.Id).GeneratedBy.GuidComb(); base.Map(x => x.Name); HasManyToMany<User>(Reveal.Property<Role>("_users")) .LazyLoad() .AsBag().Inverse() .WithTableName("UserRoles") .WithParentKeyColumn("RoleId") .WithChildKeyColumn("UserId") .Cascade.None(); } }
Notice that the role map has .Inverse() set, which indicates that it is not the owner of the relationship.
That is pretty much all there is to it, NHibernate will take care of maintaining the relationship table for you.