Getting "Object Reference Not Set" within FluentNHibernate.SeparateSubclassVisitor.SortByDistanceFrom

Adam Goss's Avatar

Adam Goss

18 Mar, 2010 07:07 PM via web

I recently converted an abstract base class to an interface and created a mapping file for that interface. Now I can't get FNH to build the configuration. Below is the stack trace. Perhaps you can shed some light on how I can debug what is going on.

[NullReferenceException: Object reference not set to an instance of an object.] FluentNHibernate.SeparateSubclassVisitor.SortByDistanceFrom(Type parentType, IEnumerable1 providers) in d:\Builds\FluentNH\src\FluentNHibernate\SeparateSubclassVisitor.cs:90 FluentNHibernate.SeparateSubclassVisitor.FindClosestSubclasses(Type type) in d:\Builds\FluentNH\src\FluentNHibernate\SeparateSubclassVisitor.cs:52 FluentNHibernate.SeparateSubclassVisitor.ProcessClass(ClassMapping mapping) in d:\Builds\FluentNH\src\FluentNHibernate\SeparateSubclassVisitor.cs:22 FluentNHibernate.MappingModel.ClassBased.ClassMapping.AcceptVisitor(IMappingModelVisitor visitor) in d:\Builds\FluentNH\src\FluentNHibernate\MappingModel\ClassBased\ClassMapping.cs:59 FluentNHibernate.MappingModel.DefaultMappingModelVisitor.Visit(ClassMapping classMapping) in d:\Builds\FluentNH\src\FluentNHibernate\MappingModel\DefaultMappingModelVisitor.cs:102 FluentNHibernate.MappingModel.HibernateMapping.AcceptVisitor(IMappingModelVisitor visitor) in d:\Builds\FluentNH\src\FluentNHibernate\MappingModel\HibernateMapping.cs:39 FluentNHibernate.PersistenceModel.ApplyVisitors(IEnumerable1 mappings) in d:\Builds\FluentNH\src\FluentNHibernate\PersistenceModel.cs:149 FluentNHibernate.PersistenceModel.BuildMappings() in d:\Builds\FluentNH\src\FluentNHibernate\PersistenceModel.cs:115 FluentNHibernate.PersistenceModel.EnsureMappingsBuilt() in d:\Builds\FluentNH\src\FluentNHibernate\PersistenceModel.cs:154 FluentNHibernate.PersistenceModel.Configure(Configuration cfg) in d:\Builds\FluentNH\src\FluentNHibernate\PersistenceModel.cs:184 FluentNHibernate.Cfg.FluentMappingsContainer.Apply(Configuration cfg) in d:\Builds\FluentNH\src\FluentNHibernate\Cfg\FluentMappingsContainer.cs:118 FluentNHibernate.Cfg.MappingConfiguration.Apply(Configuration cfg) in d:\Builds\FluentNH\src\FluentNHibernate\Cfg\MappingConfiguration.cs:56 FluentNHibernate.Cfg.FluentConfiguration.BuildConfiguration() in d:\Builds\FluentNH\src\FluentNHibernate\Cfg\FluentConfiguration.cs:110

  1. 2 Posted by Adam Goss on 19 Mar, 2010 03:43 PM

    Adam Goss's Avatar

    update: Figured out that the there is no issue with mapping an interface (at least not that I've found so far). The issue is that I am not able to create a mapping for a property that is of the Interface type...

    For example:

    public interface IFoo {

    int? ID { get; set; }
    

    } public class Foo1 : IFoo {

    public virtual int? ID { get; set; }
    

    } public class Foo2 : IFoo {

    public virtual int? ID { get; set; }
    

    }

    public class Bar {

    public virtual int? ID { get; set; }
    public virtual IFoo Foo { get; set; }
    

    }


    In the Mapping for the Bar class (seen below) I am unable to include the line commented out line.

    public class BarMap : ClassMap {

    public BarMap() {
        Table("Bar");
        Id( e => e.ID );
        // References<IFoo>( e => e.Foo).Column( "Foo_ID" );
    }
    

    }

  2. 3 Posted by Adam Goss on 22 Mar, 2010 07:08 PM

    Adam Goss's Avatar

    Failing Test Case... Not 100% sure I have the test case setup correctly, but I think it's close. I inserted this into my local copy of the SeparateSubclassVisitorFixture class.

    Note: when I change the IPastel interface to be a class the test passes.

    Happy hunting.

    Adam Goss

    [Test]
    public void Should_add_subclass_that_implements_a_parent_interface_from_a_heirarchy()
    {
        fooMapping = ( (IMappingProvider) new IColorMap() ).GetClassMapping();
    
        providers.Add( new IPastelMap() );
        providers.Add( new RedMap() );
        providers.Add( new BlueMap() );
        providers.Add( new PalePinkMap() );
        var sut = CreateSut();
        sut.ProcessClass( fooMapping );
        Assert.AreEqual( 3, fooMapping.Subclasses.Count() );
        Assert.AreEqual( 1, fooMapping.Subclasses.Where( sub => sub.Type.Equals( typeof( IPastel ) ) ).Count() );
        Assert.AreEqual( 1, fooMapping.Subclasses.Where( sub => sub.Type.Equals( typeof( Red ) ) ).Count() );
        Assert.AreEqual( 1, fooMapping.Subclasses.Where( sub => sub.Type.Equals( typeof( Blue ) ) ).Count() );
    }
    
    private interface IColor
    { }
    
    private class IColorMap : ClassMap<IColor>
    { }
    
    private interface IPastel : IColor
    { }
    
    private class IPastelMap : SubclassMap<IPastel>
    { }
    
    private class Red : IColor
    { }
    
    private class RedMap : SubclassMap<Red>
    { }
    
    private class Blue : IColor
    { }
    
    private class BlueMap : SubclassMap<Blue>
    { }
    
    private class PalePink : IPastel
    { }
    
    private class PalePinkMap : SubclassMap<PalePink>
    { }
    
  3. Support Staff 4 Posted by James Gregory on 23 Mar, 2010 02:00 PM

    James Gregory's Avatar

    Hi Adam,

    Sorry for not getting back to you sooner on this. The issue you're having is due to the way you're using interfaces in a hierarchy. We do support using an interface as a top-level class, but not lower down.

    Interfaces in the CLR don't form an inheritance structure like classes do. It looks that way in C#, but they actually don't; the hierarchy gets flattened at compile-time, so in your case IPastel doesn't actually inherit from IColor. Phil Haack goes into this in his interface inheritance esoterica post.

    We should be able to emulate an inheritance hierarchy with interfaces, but it'll take a little work. Your test fails because of a bug in the SubclassMap, but even fixing that doesn't resolve the issue entirely.

    I'll need to spend some time on this creating a proper solution, I just thought I'd update you as to what's going on.

  4. 5 Posted by Adam Goss on 23 Mar, 2010 02:21 PM

    Adam Goss's Avatar

    James,

    Thanks for the reply. The interesting thing to me is that the Unit Test I created fails on a line that occurs after the execution of the offending line in the stack trace I sent in the original post. In other words I can't replicate the original failure from your trunk build. Perhaps you're working on this area (I also noticed a difference in line numbers).

    The Color example I supplied in the test is an over simplification. I don't really need real inheritance with interfaces. What I am looking to do is have a single id authority table and have tables that correspond with selected interfaces in my application. Each object that implements one of these interfaces would have a its own table with a FK to the interfaces table along with an FK to the Authority table. Now any object that references one of the interface implementing objects can have a table with an FK for the referencing column to the interface table. This simplifies the database design quite a bit. The hierarchy is only necessary because the interface tables also need to have an FK to the ID authority table, and I can't have a duplicate subclass mapping for an object (one from the authority entity and one from the interface).

    If you see a way to get around the hierarchy I'd love to hear it. I just don't see how.

    Thanks again for the response.

    Adam

    -----Original Message-----
    From: James Gregory [mailto:***@tenderapp.com]
    Sent: Tuesday, March 23, 2010 10:01 AM
    To: Adam Goss
    Subject: [BULK] Re: Getting "Object Reference Not Set" within FluentNHibernate.SeparateSubclassVisitor.SortByDistanceFrom [Help and guidance]
    Importance: Low

  5. Support Staff 6 Posted by James Gregory on 23 Mar, 2010 02:38 PM

    James Gregory's Avatar

    Now that you mention it, the exception I was getting in the test is different from the stack-trace you provided. We have been doing some bugfixes in there, so that could be it.

    What you've described still does require an inheritance hierarchy with interfaces, albeit a much shorter one.

    What we support is the following:

    Supported inheritance hierarchy

    What I believe you're asking for is this:

    Desired inheritance?

    Which hits the problem I mentioned earlier about the interface hierarchy being flatted. If you can confirm whether my interpretation of your design is correct, I can start working on a fix.

  6. 7 Posted by Adam Goss on 23 Mar, 2010 03:17 PM

    Adam Goss's Avatar

    Yes, that is what I'm looking for as a start. One additional thing to note is that a class may implement more than one of these interfaces. This may add quite a bit more complexity.

    [Example] (http://yuml.me/e6212eb)

  7. Support Staff 8 Posted by James Gregory on 23 Mar, 2010 03:32 PM

    James Gregory's Avatar

    How would you expect that particular case to be persisted?

  8. 9 Posted by Adam Goss on 23 Mar, 2010 03:40 PM

    Adam Goss's Avatar

    Saving a Course would save a record to the Course Table and a record to the IHaveAnID table
    Saving a ScheduledCourse would have to save a Course first (thus an IHaveAnID record) and then save a record in ScheduledCourse and IHaveSchedulingRules.

    Tables would be something like this

    IHaveAnID

    ID int
    

    Course

    ID int (FK to IHaveAnID)
    Name varchar
    

    IHaveSchedulingRules

    ID int (FK to IHaveAnID)
    

    ScheduledCourse

    ID int (FK to Course, FK to IHaveSchedulingRules)
    Start DateTime
    End DateTime
    
  9. Support Staff 10 Posted by James Gregory on 23 Mar, 2010 03:53 PM

    James Gregory's Avatar

    I'm afraid to say that isn't possible. An entity can't be persisted to multiple tables in the way that you're suggesting. It's not supported in Fluent NHibernate, or NHibernate itself.

    Your design smells heavily of trying to recreate your object-model in your database; you're better looking at object or document databases if that's the thing you need.

    I have to ask, what's the purpose of the two tables for the interfaces? What's the benefit?

    Personally, I'd just have a Course table, and a ScheduledCourse table. If you have other entities that are also scheduled, they'd have their own respective tables; you can still have your IHaveSchedulingRules interface, it's just not a persistence concern.

  10. 11 Posted by Adam Goss on 23 Mar, 2010 04:04 PM

    Adam Goss's Avatar

    The idea is that a SchedulingRule object would have a Target property of type IHaveSchedulingRules. This means that the SchedulingRule.Target could reference a ScheduledCourse or a ScheduledFinalExam (or any other implementor for that matter). The SchedulingRule table would have a TargetID column that has an FK to the IHaveSchedulingRules table. Without the IHaveSchedulingRules table there's no clean way to enforce referential integrity for the Target of the rule.

    Does that make sense?

    Adam

    -----Original Message-----
    From: James Gregory [mailto:***@tenderapp.com]
    Sent: Tuesday, March 23, 2010 11:53 AM
    To: Adam Goss
    Subject: [BULK] Re: Getting "Object Reference Not Set" within FluentNHibernate.SeparateSubclassVisitor.SortByDistanceFrom [Help and guidance]
    Importance: Low

  11. Support Staff 12 Posted by James Gregory on 23 Mar, 2010 04:17 PM

    James Gregory's Avatar

    That makes sense. Welcome to the object/relational impedance mismatch.

    NHibernate has a feature called <any />, which allows polymorphic relationships across seemingly unrelated types. This is probably your best bet. Ayende covers it on his blog: any and many-to-any for collections.

    Fluent NHibernate only supports any right now, through the ReferencesAny method.

  12. 13 Posted by Adam Goss on 23 Mar, 2010 05:52 PM

    Adam Goss's Avatar

    The ReferencesAny looks real close to what I need. If I'm correct I would need to add a Type column to my tables that tells nhibernate the datatype of the referenced object.

    Are you planning to support the many-to-any in the future?

    Thanks for your help.

    Adam

    -----Original Message-----
    From: James Gregory [mailto:***@tenderapp.com]
    Sent: Tuesday, March 23, 2010 12:18 PM
    To: Adam Goss
    Subject: [BULK] Re: Getting "Object Reference Not Set" within FluentNHibernate.SeparateSubclassVisitor.SortByDistanceFrom [Help and guidance]
    Importance: Low

  13. Support Staff 14 Posted by James Gregory on 23 Mar, 2010 08:26 PM

    James Gregory's Avatar

    I believe you're correct about the type column.

    As for many-to-any, we'll implement at some point; it's not been particularly high-priority, as I think only one or two people have ever requested it. We can up the priority if need be.

  14. 15 Posted by Jay on 07 Apr, 2010 09:29 PM

    Jay's Avatar

    I'm very interested in this, too, as my domain models are largely interface-based; very little type inheritance. I get the same exception posted originally by Adam.

    The only workarounds I've found thus far are (based on the 2nd diagram posted by James):
    A. Skip declaring SubclassMaps for the 2nd level interfaces and duplicate the mapping of those interfaces' unique members in the SubclassMaps for each of their implementations in level 3.
    B. Create abstract classes that implement the 2nd level interfaces, and then have the implementations in level 3 inherit from those classes.

    One must either modify the domain model for cleaner mapping, or duplicate mapping to preserve the model. This is how it works when doing table-per-class-hierarchy, at any rate.

    Without really understanding how Fluent NHibernate works, in my mind's eye I could see syntax like the following:

    public class SecondLevelInterfaceMap : SubclassMap<ISomeInterface>
    {
        public SecondLevelInterfaceMap()
        {
            AbstractSubclassFor<InterfaceHigherUp>();
            Map(x => x.property);
        }
    }
    

    The AbstractSubclassFor<T> method would encapsulate the logic of the Abstract() method and build up the sort of fake interface inheritance hierarchy James described.

    Subsequently, any SubclassMap of a concrete type would be based on the most specific interface in the hierarchy (the one it implements that is closest to its own level).

    I'm not familiar with FNH issue tracking, but if this is or becomes an issue, I'd like to know how I may subscribe to updates.

  15. 16 Posted by Jay on 08 Apr, 2010 05:02 PM

    Jay's Avatar

    Following up from my post yesterday, I found a third workaround that I consider preferable.

    Again referencing James' first diagram above, declare the second-level interface mapping as an abstract class with a generic type parameter:

    public abstract class SecondLevelInterfaceMap<Implementation> : SubclassMap<Implementation> 
        where Implementation : SecondLevelInterface
    {
        protected SecondLevelInterfaceMap()
        {
            // map all the interface members
        }
    }
    
    // map the third-level classes by inheriting the abstract SubclassMap
    public class ConcreteClassMap : SecondLevelInterfaceMap<ConcreteClassMap>
    {
        // if there are no members beyond the interface, the class can
        // be empty, otherwise declare a constructor that calls the
        // base constructor and just map the additional members
    }
    

    This keeps the domain model intact and prevents duplicative mappings by using the abstract class that (I think) Fluent NHibernate never really looks at.

  16. 17 Posted by Jay on 08 Apr, 2010 06:24 PM

    Jay's Avatar

    ...and a correction.

    The ConcreteClassMap should be declared:

    public class ConcreteClassMap : SecondLevelInterfaceMap<ConcreteClass>
    

    (the generic type parameter is the class being mapped, not the class map)

  17. 18 Posted by Adam Goss on 08 Apr, 2010 06:33 PM

    Adam Goss's Avatar

    Seems like a good solution for multiple levels of interfaces in a hierarchy, but the show-stopping problem for me is that my ConcreteClass objects can implement more than one SecondLevelInterface. And all the ConcreteClass objects implement the same top level interface. So there is no way, I don't think, for nHibernate to know which path to follow.

    Adam

  18. Support Staff 19 Posted by James Gregory on 01 May, 2010 12:57 PM

    James Gregory's Avatar

    FYI, there's now an Extends method in SubclassMap which might help in situations where FNH can't figure out the hierarchy.

  19. James Gregory closed this discussion on 01 May, 2010 12:57 PM.

Comments are currently closed for this discussion. You can start a new one.