0

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).

Community
  • 1
  • 1
Katie Kilian
  • 6,815
  • 5
  • 41
  • 64

1 Answers1

1

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
    }
}
Firo
  • 30,626
  • 4
  • 55
  • 94
  • Brilliant. It worked like a charm. I did have to change `.AsDictionary` to `.ToDictionary`, but other than that it was perfect. Thanks so much! – Katie Kilian Feb 16 '12 at 15:34
  • Note to anyone using this code: I also had a situation where my table names were getting a backtick around them. I had to filter the string to remove the backticks, like this: `return "FK" + tablenames[type].Replace( "\`", "" );` – Katie Kilian Feb 18 '12 at 23:00