开发者

Fluent NHibernate automapping: How to custom subclass table name and key column name?

开发者 https://www.devze.com 2023-02-19 23:02 出处:网络
I think this must have been asked before, but I have googled for an hour but couldn\'t find the answer.

I think this must have been asked before, but I have googled for an hour but couldn't find the answer.

Let's say I have the following 2 models:

public class Organism
{
    public virtual int Id { get; private set; }
}

public class Animal : Organism
{
}

I want Fluent NHibernate to create the following tables for me:

OrganismTable
    OrganismId

AnimalTable
    AnimalId

This can be easily achieved by using manual mapping:

public class OrganismMappinig : ClassMap<Organism>
{
    public OrganismMappinig()
    {
        Table("OrganismTable");
        Id(x => x.Id).Column("OrganismId");
    }
}

public class AnimalMapping : SubclassMap<Animal>
{
    public AnimalMapping()
    {
        Table("AnimalTable");
        KeyColumn("AnimalId");
    }
}  

But I can't get the same result using automapping. I tried to add the following conventions:

public class TableNameConvension : IClassConvention, IClassConventionAcceptance
{
    public void Apply(IClassInstance instance)
    {
        instance.Table(instance.EntityType.Name + "Table");
    }

    public void Accept(IAcceptanceCriteria<IClassInspector> criteria)
    {
        criteria.Expect(x => x.TableName, Is.Not.Set);
    }
}

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

It created these 2 tables:

OrganismTable (correct)
    OrganismId (correct)

Animal (wrong, should be "AnimalTable")
    Organism_id (wrong, should be "AnimalId")

I also tried to add:

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

        return property.Name + "Id";
    }
}

It created these 2 tables:

OrganismTable (correct)
    OrganismId (correct)

Animal (wrong, should be "AnimalTable")
    OrganismId (wrong, should be "AnimalId")

I also tr开发者_Go百科ied to add:

public class AnimalOverride : IAutoMappingOverride<Animal>
{
    public void Override(AutoMapping<Animal> mapping)
    {
        mapping.Table("AnimalTable");
        mapping.Id(x => x.Id).Column("AnimalId");
    }
}

It created the following tables:

OrganismTable (correct)
    OrganismId (correct)

AnimalTable (correct)
    OrganismId (wrong, should be "AnimalId")

This correctly set table name to "AnimalTable" (but this takes too much manual typing, would be great if there is a convention that can get the same result), but failed to set key column name to "AnimalId".

Below is the rest of my code:

class Program
{
    static void Main(string[] args)
    {
        ISessionFactory sessionFactory = Fluently.Configure()
            .Database(MsSqlConfiguration
                          .MsSql2008.ConnectionString(connectionString)
                          .ShowSql())
            .Mappings(m => m.AutoMappings.Add(
                AutoMap.Assemblies(typeof(Organism).Assembly)
                    .Conventions.AddAssembly(typeof(Program).Assembly)
                    .UseOverridesFromAssembly(typeof(Program).Assembly)))
            .ExposeConfiguration(BuildSchema)
            .BuildConfiguration()
            .BuildSessionFactory();
    }

    static void BuildSchema(Configuration cfg)
    {
        new SchemaExport(cfg).Create(false, true);
    }
}

Any ideas? Thanks.


When FluentNHibernate is automapping, it uses table-per-concrete-class for mapping inheritance hierarchies. Since Organism and Animal are both concrete types, it generates two tables, OrganismTable and AnimalTable. OrganismTable stores all the properties common with all Organisms. AnimalTable only stores the properties added by the Animal subclass. The PK on AnimalTable is also a FK to OrganismTable. Hence it being affected by your FK convention. Note that PKNameConvention is only called for Organism and not for Animal. Similarly FKColumnNameConvention is only called by Animal. Because the Animal FK is only used internally to hook up the inheritance hierarchy, the property is null. Hence you're getting OrganismId as the FK name. To set this PK/FK, you need to use an IJoinedSubclassConvention. (I've included your TableNameConvention and PrimaryKeyNameConvention for completeness.)

public class TableNameConvension : IClassConvention, IClassConventionAcceptance {
    public void Apply(IClassInstance instance) {
        instance.Table(instance.EntityType.Name + "Table");
    }

    public void Accept(IAcceptanceCriteria<IClassInspector> criteria) {
        criteria.Expect(x => x.TableName, Is.Not.Set);
    }
}
public class PrimaryKeyNameConvention : IIdConvention {
    public void Apply(IIdentityInstance instance) {
        instance.Column(instance.EntityType.Name + "Id");
    }
}
public class JoinedSubclassIdConvention : IJoinedSubclassConvention
{
    public void Apply(IJoinedSubclassInstance instance) {
        instance.Table(instance.EntityType.Name + "Table");
        instance.Key.Column(instance.EntityType.Name + "Id");
    }
}

This generates:

OrganismTable
    OrganismId

AnimalTable
    AnimalId

with a FK from AnimalTable.AnimalId to OrganismTable.OrganismId.

Please note that table-per-concrete-class is not my preferred choice for inheritance mapping due to the number of joins required when querying entities. Often table-per-subclass or table-per-class-hierarchy are better choices. I'll leave it as an exercise for the reader to investigate the differences in the NHibernate documentation on inheritance strategies.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号