views:

253

answers:

3

New to NHibernate. Having trouble wrapping my head around how to map this legacy table.

CREATE TABLE [dbo].[CmnAddress](
[addressId] [int] NOT NULL,
[objectType] [varchar](63) NULL,
[objectId] [int] NULL,
[addressType] [varchar](7) NULL,
[recordStatus] [char](1) NULL,
[fromDate] [int] NULL,
[toDate] [int] NULL,
[onStreet] [varchar](254) NULL,
[atStreet] [varchar](254) NULL,
[unit] [varchar](30) NULL,
[city] [varchar](254) NULL,
[state] [varchar](30) NULL,
[zipCode] [varchar](30) NULL,
)

There is also a "CmnPerson" table that I have mapped to a Person class. I need the Person class to contain a list of Addresses, where the objectType column contains "CmnPerson" and the objectId field matches my Person.Id ("CmnPerson.personId") field.

I am also later going to have to create a Contact class that also contains a list of Addresses where the objectType column contains "CmnContact".

I'm having a very tough time figuring out if I should be using the Any mapping or class-hierarchy-per-table with discrimination on subcolumns? Or if either of those will even meet my needs.

Can anyone show my how to map this Address class? Fluent config would be preferable.

ADDED INFO:

The following classes and mappings almost work, but the Addresses list return ALL rows from the CmnAddress table with a matching objectId, regardless of the objectType field's value. I think I could use an ApplyFilter on the HasMany mapping for Person.Addresses, but that doesn't seem like the "right" way.

More added info: I was able to resolve this last issue by chaining AlwaysSelectWithValue() on after the call to DiscriminateSubClassesOnColumn(...)

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

 public virtual IList<PassClient> PassClients { get; set; }
 public virtual IList<PersonAddress> Addresses { get; set; }
}

public class PersonMap : ClassMap<Person>
{
 public PersonMap() {
  Table("CmnPerson");
  Id(x => x.Id).Column("personId");
  Map(x => x.LastName);
  Map(x => x.FirstName);
  Map(x => x.MiddleName);
  Map(x => x.Gender);

  HasMany(x => x.PassClients).KeyColumn("personId");
  HasMany(x => x.Addresses).KeyColumn("objectId");
 }
}

abstract public class Address
{
 public virtual int Id { get; private set; }
 public virtual string StreetNo { get; set; }
 public virtual string OnStreet { get; set; }
 public virtual string Unit { get; set; }
 public virtual string City { get; set; }
 public virtual string State { get; set; }
 public virtual string ZipCode { get; set; }
}

public class PersonAddress : Address { 
 public virtual Person Person { get; set; }
}

public class AddressMap : ClassMap<Address>
{
 public AddressMap() {
  Table("CmnAddress");

  Id(x => x.Id).Column("addressId");
  Map(x => x.StreetNo);
  Map(x => x.OnStreet);
  Map(x => x.Unit);
  Map(x => x.City);
  Map(x => x.State);
  Map(x => x.ZipCode);

  DiscriminateSubClassesOnColumn("objectType").AlwaysSelectWithValue();
 }
}

public class PersonAddressMap : SubclassMap<PersonAddress>
{
  public PersonAddressMap() {
  DiscriminatorValue("CmnPerson");

  References(x => x.Person).Column("objectId");
  }
}
A: 

Address class

public class Address
{
    public virtual int Id { get; set; }
    public virtual Person Person { get; set; }
    // etc.
}

Person class

public class Person
{
    public Person()
    {
        Addresses = new List<Address>();
    }

    public virtual int Id { get; set; }
    // etc.
    public virtual IList<Address> Addresses { get; set; }
}

AddressMap

public class AddressMap : ClassMap<Address>
{
    public AddressMap()
    {
        Table("CmnAddress");
        Id(x => x.Id).Column("addressId");
        // etc.
        References(x => x.Person);
    }
}

To separate the difference between Address => Person and Address => Contact, you'll want to read up on NHibernate's polymorphism and discriminate sub classes based on column.

Jarrett Meyer
qstarin
+1  A: 

Sorry, am not familiar with fluent mappings, however, one way to do it would be:

  • Create an Address abstract class with properties corresponding to all the columns in your table except for objectType
  • Create a PersonAddress class which extends Address
  • Create a ContactAddress class which extends Address

  • Map all the columns of CmnAddress to properties of the Address class in the normal way, except for objectType which you declare as the discriminator column

  • Map PersonAddress as a subclass of Address with discriminator value "CmnPerson"
  • Map ContactAddress as a subclass of Address with a discriminator value of "CmnContact"

In code, your Person class would hold a list of PersonAddresses and your Contact class would hold a list of ContactAddresses

alasdairg
+1  A: 

You can not use the Any-mapping when it's a has many relation. It can be used, when an address can point to different kinds of objects - like a person, an order or other things not related.

To map the hierarachy you can do it like this:

public class CmnAddressMap : ClassMap<CmnAddress>
{
    public CmnAddressMap()
    {
        Id(x => x.addressId);
        Map(x => x...);

        DiscriminateSubClassesOnColumn("objectType");
    }
}

public class PersonAdressMap : SubclassMap<PersonAddress>
{
    public PersonAdressMap()
    {
        DiscriminatorValue("objectType1");
    }
}

public class ContactAdressMap : SubclassMap<ContactAddress>
{
    public ContactAdressMap()
    {
        DiscriminatorValue("objectType2");
    }
}

Have an abstract CmnAddress with all the fields (map all the fields in the CmnAdressMap for example) and two subclasses named PersonAddress and ContactAddress.

And the person should then have a collection like IList which should mapped with HasMany. And you should be done.

asgerhallas
I came to this exact same answer just now - just finished testing it.It does compile and run, but the Person.Addresses list returns ALL rows from the CmnAddress table with a matching id value, regardless of the value in the discriminating column.Any ideas on how to resolve that?
qstarin
To which the answer is to chain the AlwaysSelectWithValue() call after the DiscriminateSubClassesOnColumn(...) call. Or at least it seems to have the desired result.
qstarin
Sorry for my late reply. That's pretty strange. I just ran a test with your scenario, and there is no need for me to specify AlwaysSelectWithValue().
asgerhallas
Oh. Now I see your new info. Have you any rows in the adresses table, that has no discriminator or a discriminator value that is not mapped to subclass?
asgerhallas
I do in fact have discriminator values that are not mapped to subclasses, which is the reason I have to specifiy AlwaysSelectWithValue().
qstarin