Search code examples
c#nhibernatefluent-nhibernatefluent-nhibernate-mapping

Setting up one to many relationship with Fluent Nhibernate


I'm having a bit of an argument with my co-worker that I can't seem to find an answer to yet this is very basic stuff.

Establishing one-to-many relationship in Fluent Nhibernate entity.

Let's take Roles and users for example. A role can be assigned to multiple users so I made my entity guts looks like:

public class User
{
    [Required]
    public virtual string FirstName { get; set; }
    public virtual Role Role { get; set; }
}

and role

public class Role
{
    [Required]
    public virtual string Name { get; set; }
    public virtual IList<User> Users{ get; set; }

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

As you can see I'm referencing a collection of Users in roles, kind of saying that each role will have multiple users. User entity has that Role entity reference needed to identify what Role does a user belong to.

In my opinion this is the correct way to link and my co-worker says that having a Role reference for users will create a circular reference. Who's right ?

I tried finding the answer online. I think this question tells me that I'm right: Fluent NHibernate Many to one mapping

But then I looked at a Fuent Nhibernate sample project here https://github.com/jagregory/fluent-nhibernate/tree/master/src/Examples.FirstAutomappedProject/Entities and I don't an example of what I'm trying to implement. Can you guys advise or help me find a document explaining the correct way ? Am I right? Thank you.


Solution

  • What you are proposing here is entirely possible and allowable within the nHibernate framework. Obviously you have listed the models rather than the mapping files but Fluent nHibernate allows for you to configure your mappings in this way without issue.

    Whether you actually choose to map a relationship in this way is entirely down to personal preference and the specific scenario. I have mapped models in this way but equally have chosen not to, mainly because in some instances it doesn't make sense to overly complicate the object graph. Take for example a look-up table (e.g. Culture or Locale) that is referenced many times across multiple tables in a database, when mapping this I would have a Culture property in each one of the parent models but would not have collections of parent objects in the Culture model - it just doesn't make sense.

    You also need to consider loading data through your persistence layer - if you create this sort of relationship and just need a simple list of roles you need to consider when, or if , the users collection is populated - you can specify eager or late loading but in my experience specifying eager loading with a corresponding Fetch command in the Queries can result in a more optimized call to the database.

    Basically what I am saying is that there is no absolute "correct way" when deciding how to define your mappings - you really need to balance a rich object model vs. query performance, but your specific example is perfectly acceptable if you need it.

    I have just re-read your question and I am not sure if you are also asking for examples of mappings configured in this way so if you do want some examples let me know and I'll put some together for you.

    To achieve the relationship you describe you would need the following map classes (I have added Id properties as I guess you have those):

    For the Roles:

    public class RoleMap : ClassMap<Role>
    {
        public RoleMap()
        {
              Table(@"Roles");
              Id(x => x.Id).GeneratedBy.Assigned();
              Map(x => x.Name).Column("Name");
              HasMany<User>(x => x.Users)
                .Inverse()
                .KeyColumns.Add("RoleId", mapping => mapping.Name("RoleId"));
        }
    }
    

    For the Users:

    public class UserMap : ClassMap<User>
    {
       public UserMap()
        {
              Table(@"Users");
              Id(x => x.Id).GeneratedBy.Assigned();
              Map(x => x.FirstName);
              Map(x => x.RoleId);    
              References(x => x.Role)
                .Class<Role>()
                .Columns("RoleId");
        }
    }
    

    I wouldn't get too hung up on "your method vs their method" - the above is perfectly acceptable, and depends on your requirements when you come to use the object model in your code. Alternatively if you don't want a Users collection in the Role Map just remove that property and the associated HasMany mapping declaration. One thing to note from the above is that the .Inverse() specification has delegated management of the relationship between a Role and a User to the Users entity, which makes sense as basically the process of adding or editing an existing user and providing a RoleId will forge the relationship.

    Hope this helps somewhat if you have any more specific questions let me know