Katie Kilian
Katie Kilian

Reputation: 6995

Fluent NHibernate: Custom ForeignKeyConvention not working with explicitly specified table names

EDIT: for the tl;dr crowd, my question is: How do I access the mappings from inside the ForeignKeyConvention in order to determine the table name that a given type is mapped to?

The long version:

I am using Fluent NHibernate to configure NHibernate, and I have a custom foreign key convention that is failing when I alias tables and columns.

My tables use a convention where the primary key is always called "PK", and the foreign key is "FK" followed by the name of the foreign key table, e.g., "FKParent". For example:

CREATE TABLE OrderHeader (
    PK INT IDENTITY(1,1) NOT NULL,
    ... 
)

CREATE TABLE OrderDetail (
    PK INT IDENTITY(1,1) NOT NULL, 
    FKOrderHeader INT NOT NULL,
    ...
)

To make this work, I've built a custom ForeignKeyConvention that looks like this:

public class AmberForeignKeyConvention : ForeignKeyConvention
{
    protected override string GetKeyName( Member member, Type type )
    {
        if ( member == null )
            return "FK" + type.Name;  // many-to-many, one-to-many, join

        return "FK" + member.Name; // many-to-one
    }
}

This works so long as my entities are named the same as the table. But it breaks when they aren't. For example, if I want to map the OrderDetail table to a class called Detail, I can do so like this:

public class DetailMap : ClassMap<Detail>
{
    public DetailMap()
    {
        Table( "OrderDetail" );
        Id( o => o.PK );
        References( o => o.Order, "FKOrderHeader" );
        ...
    }
}

The mapping works for loading a single entity, but when I try to run any kind of complicated query with a join, it fails, because the AmberForeignKeyConvention class is making incorrect assumptions about how the columns are mapped. I.e., it assumes that the foreign key should be "FK" + type.Name, which in this case is Order, so it calls the foreign key "FKOrder" instead of "FKOrderHeader".

So as I said above: My question is, how do I access the mappings from inside the ForeignKeyConvention in order to determine a given type's mapped table name (and for that matter, their mapped column names, too)? The answer to this question seems to hint at the right direction, but I don't understand how the classes involved work together. When I look through the documentation, it's frightfully sparse for the classes I've looked up (such as the IdMapping class).

Upvotes: 0

Views: 736

Answers (1)

Firo
Firo

Reputation: 30813

the idea is to load the mappings

public class AmberForeignKeyConvention : ForeignKeyConvention
{
    private static IDictionary<Type, string> tablenames;

    static AmberForeignKeyConvention()
    {
        tablenames = Assembly.GetExecutingAssembly().GetTypes()
            .Where(t => typeof(IMappingProvider).IsAssignableFrom(t))
            .ToDictionary(
                t => t.BaseType.GetGenericArguments()[0], 
                t => ((IMappingProvider)Activator.CreateInstance(t)).GetClassMapping().TableName);
    }

    protected override string GetKeyName( Member member, Type type )
    {
        return "FK" + tablenames[type]; // many-to-one
    }
}

Upvotes: 1

Related Questions