views:

94

answers:

2

I'm a bit of a Fluent nHibernate newbie, so forgive me is this is easy and I'm just missing it.

I have a class hierarchy with Customer and User which both inherit from Person. Customer and User each have their own table and mapping and everything is working great.

My problem is that Customer and User need to have a list of Contacts, which will be of type IList. So Customer and Users will each have a list of contact that can contain a mix of Customers and Users.

I'm kinda stumped on how to map this and would appreciate any advice that is offered.

Thanks!

+1  A: 

I believe you need a IList<Person> Contacts {get;set;} and a type (discriminator) so that you know what to cast it to. Maybe it's a hack but that's how I would do it.

EDIT: Let's say your entities would look like below.

public abstract class Person
{
    public virtual int Id { get; private set; }
    public virtual string FirstName { get; set; }
    public virtual string LastName { get; set; }
}

public class Customer : Person
{
    public virtual string Foo { get; set; }
}

public class Contact : Person
{
    public virtual string Bar { get; set; }
}

The easy (hacky) way of solving your problem would be a discriminator and move everything to the same table. The mapping for this could look like:

public class PersonMap : ClassMap<Person>
{
    public PersonMap()
    {
     Id(x => x.Id).GeneratedBy.Native();
     Map(x => x.FirstName).Length(40);
     Map(x => x.LastName).Length(100);
     DiscriminateSubClassesOnColumn("TypeOfPerson");
    }
}

public class CustomerMap : SubclassMap<Customer>
{
    public CustomerMap()
    {
     Map(x => x.Foo);
    }
}

public class ContactMap : SubclassMap<Contact>
{
    public ContactMap()
    {
     Map(x => x.Bar);
    }
}

If you generate your schema based on that model you end up with:

create table [Person] (
  Id                 INT   IDENTITY   NOT NULL,
  BillingDetailsType NVARCHAR(255)   not null,
  FirstName          NVARCHAR(40)   null,
  LastName           NVARCHAR(100)   null,
  Foo                NVARCHAR(255)   null,
  Bar                NVARCHAR(255)   null,
    primary key ( Id ))

I do understand that this is maybe not ideal certainly so if your boss (like mine) is a database guru that refuses to leave the database in such a state. All you need to do then is to remove the DiscriminateSubClassesOnColumn("TypeOfPerson"); and you should instead end up with a table structure like below should you generate the schema from your code:

-- statement #1
create table [Person] (
  Id        INT   IDENTITY   NOT NULL,
  FirstName NVARCHAR(40)   null,
  LastName  NVARCHAR(100)   null,
    primary key ( Id ))

-- statement #2
create table [Customer] (
  Person_id INT   not null,
  Foo       NVARCHAR(255)   null,
    primary key ( Person_id ))

-- statement #3
create table [Contact] (
  Person_id INT   not null,
  Bar       NVARCHAR(255)   null,
    primary key ( Person_id ))

-- statement #4
alter table [Customer]
 add constraint FKFE9A39C0B58BA2A5 foreign key ( Person_id ) references [Person]

With this last schema you need to figure out a way to determine which subclass the selected Person really is if you ever need an IList<Person> but I am sure you can figure that out. :) Hope this helps!

mhenrixon
How would I set that up?Would a better approach be to refactor my Customers and Users into one table a la this: http://ang3lfir3.wordpress.com/2009/08/04/table-per-class-hierarchy-inheritance-mapping-with-fluent-nhibernate/ ?Thanks
James Bender
Added some more info ;)
mhenrixon
I'm the lead, and we don't have a DBA, so I have no problem putting both in the same table as it seems the easiest approach. Thanks!
James Bender
A: 

I have done this by Party model. This way your abstraction is always Party:

public abstract class Party
{
     private IList<Party> _contacts = new List<Party>();

     public int Id {get; set;}
     public abstract string DisplayName { get; }
     public IEnumerable<Party> Contacts { get { return _contacts; } }  
}

public class Person 
{
     public string FirstName {get; set;}
     public string LastName {get; set;}

     public override string DisplayName 
     {
         get { return FirstName + " " + LastName; }
     }
}

public class Organization
{
     public string Name {get; set;}

     public override string DisplayName 
     {
         get { return Name; }
     }
}

For mapping there are two possible strategies: One table per inheritance tree; one table per class.

public class PartyMap : ClassMap<Party>
{
    public PartyMap()
    {
        Id(x => x.Id).GeneratedBy.Native();
        HasManyToMany(x => x.Contacts).Access.CamelCaseField(Prefix.Underscore);
    }
}

public class PersonMap : JoinedSubClassPart<Person>
{
    public PersonMap()
    {
        Map(x => x.FirstName).Length(40);
        Map(x => x.LastName).Length(100);
    }
}

public class OrganizationMap : JoinedSubClassPart<Organization>
{
    public OrganizationMap()
    {
        Map(x => x.Name).Length(40);
    }
}

That should create 4 tables: Party, Person, Organization and Contact

Marek Tihkan

related questions