Automapping and subclassing

406 views
Skip to first unread message

Asbjørn Ulsberg

unread,
Sep 26, 2011, 8:46:39 AM9/26/11
to Fluent NHibernate
Hey.

I have a lot of entities in my domain model that I want to map with subclass mapping. I'm really uncertain how this is best done in FNH these days. The only examples I find online are for fluent mappings, not for automapping. I'd like the conventions I've defined to apply to all subclassed entities, but if I try something like this in an IAutoMappingOverride:

   mapping.JoinedSubClass<Child>("Type");

The Child object won't be mapped according to conventions. If I try the following:

  mapping.SubClass<Child>(1, x => { })

FNH throws the following exception:

  System.MissingMethodException: Constructor on type 'FluentNHibernate.Automapping.AutoSubClassPart`1[[Child]]' not found

Creating an implementation of AutoSubClassPart<Child> doesn't help either, and I'm not sure this is a class I'm supposed to implement either way. I guess not. I'm having difficulties figuring out how subclass mapping can be done combined with automapping. Is it possible or do I have to revert to static fluent mapping (that disregard my lovely conventions)?

--  
Asbjørn Ulsberg           -=|=-        asb...@ulsberg.no
«He's a loathsome offensive brute, yet I can't look away»

Asbjørn Ulsberg

unread,
Sep 27, 2011, 4:09:48 AM9/27/11
to Fluent NHibernate
After some digging, I've found IJoinedSubclassConvention that is being invoked when mapping. I've found ISubclassConvention as well, but that is not being invoked. I guess joined subclass is the default and that any other convention needs to be configured somewhere else(?).

Either way, I'm stumped on what I'm supposed to do in the IJoinedSubclassConvention.Apply() method. I have a IJoinedSubclassInstance that doesn't look like any other mapping object, so I'm wondering what I'm supposed to do with it to make joined subclasses follow conventions wrt foreign key naming, ID naming, etc. Does anyone have any examples of an IJoinedSubclassConvention implementation? The API documentation is rather ... sparse:


:-)

--  
Asbjørn Ulsberg           -=|=-        asb...@ulsberg.no
«He's a loathsome offensive brute, yet I can't look away»

James Gregory

unread,
Sep 27, 2011, 11:00:10 AM9/27/11
to fluent-n...@googlegroups.com
Is it just foreign key naming you're doing, or more? Have you seen the ForeignKeyConvention base class? You might be able to use that instead; but either way, that class implements IJoinedSubclassConvention so it might give you some ideas.

Documentation on this area is sparse; frankly, because it's crap.

Isaac Cambron

unread,
Sep 28, 2011, 12:44:48 AM9/28/11
to fluent-n...@googlegroups.com
I looked into this for a while (original group post here). Basically, subclassing from an override doesn't work at all. I took a stab at fixing it (branch is here, though I'm not sure what state it's in), and wasn't able to make it work within the timebox I had for it. In fact, it turned out to be really complicated and probably the wrong approach, and I may have to start over. In fact, after attacking it from a few different angles, I'm not even sure it *should* be possible; the Subclass<> method deprecation is actually right, but in the meantime we don't have any other solution. But I'll probably have another go at it in case I missed something.

The specific error you're getting isn't hard to fix though, if I'm right about what it is. Basically, AutoSubClassPart only takes a string discriminator in the constructor for some reason, and the reflection code that tries to find it is failing. My branch has a fix for that here, and also some tests about it. OTOH, I suspect that if you pull out that fix, it will only get you to the next failure.

TL;DR - mapping.Subclass<Child>(1, () => {}) is very broken.

-Isaac

On Tue, Sep 27, 2011 at 11:00 AM, James Gregory <jagreg...@gmail.com> wrote:
Is it just foreign key naming you're doing, or more? Have you seen the ForeignKeyConvention base class? You might be able to use that instead; but either way, that class implements IJoinedSubclassConvention so it might give you some ideas.

Documentation on this area is sparse; frankly, because it's crap.

--
You received this message because you are subscribed to the Google Groups "Fluent NHibernate" group.
To view this discussion on the web visit https://groups.google.com/d/msg/fluent-nhibernate/-/9H4Fa-MP1lwJ.

To post to this group, send email to fluent-n...@googlegroups.com.
To unsubscribe from this group, send email to fluent-nhibern...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/fluent-nhibernate?hl=en.

Asbjørn Ulsberg

unread,
Sep 28, 2011, 11:19:24 AM9/28/11
to fluent-n...@googlegroups.com
Thanks for your insights!

The things I'm mapping is the Id and foreign key. I've hacked my way around all of this by implementing IJoinedSubclassConvention like this:

public class JoinedSubclassConvention : IJoinedSubclassConvention
{
private static readonly IList<ClassMapping> joinedSubclasses;


static JoinedSubclassConvention()
{
joinedSubclasses = new List<ClassMapping>();
}


public static void Add<T>(AutoMapping<T> mapping)
{
ClassMapping classMapping = ((IMappingProvider)mapping).GetClassMapping();
joinedSubclasses.Add(classMapping);
}


public void Apply(IJoinedSubclassInstance instance)
{
string id = joinedSubclasses
.Where(mapping => mapping.Type == instance.EntityType)
.Select(mapping => mapping.Id).OfType<ColumnBasedMappingBase>()
.SelectMany(mapping => mapping.Columns)
.Where(column => column.Name != null && column.Name.EndsWith("Id"))
.Select(idColumn => idColumn.Name)
.FirstOrDefault();

if (id != null)
{
instance.Key.Column(id);
instance.Key.ForeignKey(String.Format("FK_{0}_{1}", instance.EntityType.Name, id));
}

if (instance.EntityType.IsAbstract)
instance.Abstract();
}
}

And then in all IAutoMappingOverride implementations, I do this:

public void Override(AutoMapping<X> mapping)
{
                // Do the mapping
  JoinedSubclassConvention.Add(mapping);
}

It's lots of things that can be done in IAutoMappingOverride that isn't taken into consideration in my JoinedSubclassConvention, but this works for Id and foreign keys at least. I would love if this part of FNH could get some love in the future and I would be more than happy to test anything that improves on how this works at the moment.

Just being able to more easily figure out what an IAutoMappingOverride has done, and be able to extract this would be awesome. Even more awesome would be if IAutoMappingOverride would actually be useful for subclass-mapped entities, so when calling mapping.Id(x => x.Id, "MyId"), the ID column is named "MyId", etc.

--  
Asbjørn Ulsberg           -=|=-        asb...@ulsberg.no
«He's a loathsome offensive brute, yet I can't look away»

Asbjørn Ulsberg

unread,
Sep 29, 2011, 7:33:09 AM9/29/11
to fluent-n...@googlegroups.com
Just to add to the problems with joined subclasses, it seems that it's impossible to use AutoMappingTester<T> on a class that is subclass-mapped. FNH at least throws the following exception in my face:

    System.InvalidOperationException : Could not find mapping for class 'Subclass'
        at FluentNHibernate.Testing.DomainModel.Mapping.MappingTester`1.ForMapping(ClassMap`1 classMap) in fluent-nhibernate\src\FluentNHibernate.Testing\DomainModel\Mapping\MappingTester.cs: line 73
        at FluentNHibernate.Testing.Automapping.AutoMappingTester`1..ctor(AutoPersistenceModel mapper) in fluent-nhibernate\src\FluentNHibernate.Testing\AutoMapping\AutoMappingTester.cs: line 16

:-(

--  
Asbjørn Ulsberg           -=|=-        asb...@ulsberg.no
«He's a loathsome offensive brute, yet I can't look away»

Asbjørn Ulsberg

unread,
Sep 30, 2011, 2:45:34 AM9/30/11
to fluent-n...@googlegroups.com
Ah, I'm such a dumb-ass. The reason AutoMappingTester<Subclass> doesn't work is because there isn't a separate mapping (that is, an HBM.XML) for the Subclass. It exists only as a <joined-subclass /> child node of Superclass' mapping file, so if I want to test how Subclass is mapped, I have to create AutoMappingTester<Superclass> and drill into the <joined-subclass /> element.

--  
Asbjørn Ulsberg           -=|=-        asb...@ulsberg.no
«He's a loathsome offensive brute, yet I can't look away»
Reply all
Reply to author
Forward
0 new messages