FluentNHibernate: How to translate HasMany(x => x.Addresses).KeyColumn("PersonId") into automapping
Asked Answered
E

1

1

Say I have these models:

public class Person
{
    public virtual int Id { get; private set; }
    public virtual ICollection<Address> Addresses { get; private set; }
}

public class Address
{
    public virtual int Id { get; private set; }
    public virtual Person Person { get; set; }
}

I want FluentNHibernate to create the following tables:

Person
    PersonId
Address
    AddressId
    PersonId

This can be easily achieved by using fluent mapping:

public class PersonMapping : ClassMap<Person>
{
    public PersonMapping()
    {
        Id(x => x.Id).Column("PersonId");
        HasMany(x => x.Addresses).KeyColumn("PersonId");
    }
}

public class AddressMapping : ClassMap<Address>
{
    public AddressMapping()
    {
        Id(x => x.Id).Column("AddressId");
        References(x => x.Person).Column("PersonId");
    }
}

I want to get the same result by using auto mapping. I tried the following conventions:

class PrimaryKeyNameConvention : IIdConvention
{
    public void Apply(IIdentityInstance instance)
    {
        instance.Column(instance.EntityType.Name + "Id");
    }
}

class ReferenceNameConvention : IReferenceConvention
{
    public void Apply(IManyToOneInstance instance)
    {
        instance.Column(string.Format("{0}Id", instance.Name));
    }
}

But I sadly found it created the following tables:

Person
    PersonId
Address
    AddressId
    PersonId
    Person_id // this column should not exist

Below is the rest of my code:

ISessionFactory sessionFactory = Fluently.Configure()
    .Database(MsSqlConfiguration.MsSql2008.ConnectionString(connectionString))
    .Mappings(m =>
                m.AutoMappings.Add(AutoMap.Assemblies(typeof(Person).Assembly)
                    .Conventions.Add(typeof(PrimaryKeyNameConvention))
                    .Conventions.Add(typeof(ReferenceNameConvention)))

                //m.FluentMappings
                //    .Add(typeof (PersonMapping))
                //    .Add(typeof (AddressMapping))
    )
    .ExposeConfiguration(BuildSchema)
    .BuildConfiguration()
    .BuildSessionFactory();

Any ideas? Thanks.

Earring answered 22/5, 2011 at 22:38 Comment(0)
S
5

You need a foreign key convention:

public class SimpleForeignKeyConvention : ForeignKeyConvention
    {
        protected override string GetKeyName(Member property, Type type)
        {
            if(property == null)
                return type.Name + "Id";
            return property.Name + "Id";
        }
    }

Also, your reference convention should look more like:

instance.Column(instance.Property.Name + "Id");
Shrunken answered 22/5, 2011 at 22:41 Comment(3)
Thanks for your ultra fast answer!Earring
This seems to only work if your Reference side of the mapping uses the type.Name as its property name. For example, if the "Person" property of Address was named "MainContact", you'd get two different key columns generated by the above: MainContactId from the IReferenceConvention and PersonId from the ForeignKey convention invoked for the HasMany mapping.Microsurgery
Also, ForeignKeyConvention implements IReferenceConvention, so using both may result in multiple column names being mapped for a single property.Microsurgery

© 2022 - 2024 — McMap. All rights reserved.