Getting "Object Reference Not Set" within FluentNHibernate.SeparateSubclassVisitor.SortByDistanceFrom
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(IEnumerable
1 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
Comments are currently closed for this discussion. You can start a new one.
2 Posted by Adam Goss on 19 Mar, 2010 03:43 PM
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 {
} public class Foo1 : IFoo {
} public class Foo2 : IFoo {
}
public class Bar {
}
In the Mapping for the Bar class (seen below) I am unable to include the line commented out line.
public class BarMap : ClassMap {
}
3 Posted by Adam Goss on 22 Mar, 2010 07:08 PM
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
Support Staff 4 Posted by James Gregory on 23 Mar, 2010 02:00 PM
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 fromIColor
. 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.
5 Posted by Adam Goss on 23 Mar, 2010 02:21 PM
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
Support Staff 6 Posted by James Gregory on 23 Mar, 2010 02:38 PM
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:
What I believe you're asking for is this:
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.
7 Posted by Adam Goss on 23 Mar, 2010 03:17 PM
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)
Support Staff 8 Posted by James Gregory on 23 Mar, 2010 03:32 PM
How would you expect that particular case to be persisted?
9 Posted by Adam Goss on 23 Mar, 2010 03:40 PM
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
Course
IHaveSchedulingRules
ScheduledCourse
Support Staff 10 Posted by James Gregory on 23 Mar, 2010 03:53 PM
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 aScheduledCourse
table. If you have other entities that are also scheduled, they'd have their own respective tables; you can still have yourIHaveSchedulingRules
interface, it's just not a persistence concern.11 Posted by Adam Goss on 23 Mar, 2010 04:04 PM
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
Support Staff 12 Posted by James Gregory on 23 Mar, 2010 04:17 PM
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 theReferencesAny
method.13 Posted by Adam Goss on 23 Mar, 2010 05:52 PM
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
Support Staff 14 Posted by James Gregory on 23 Mar, 2010 08:26 PM
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.
15 Posted by Jay on 07 Apr, 2010 09:29 PM
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
SubclassMap
s for the 2nd level interfaces and duplicate the mapping of those interfaces' unique members in theSubclassMap
s 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:
The
AbstractSubclassFor<T>
method would encapsulate the logic of theAbstract()
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.
16 Posted by Jay on 08 Apr, 2010 05:02 PM
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:
This keeps the domain model intact and prevents duplicative mappings by using the abstract class that (I think) Fluent NHibernate never really looks at.
17 Posted by Jay on 08 Apr, 2010 06:24 PM
...and a correction.
The
ConcreteClassMap
should be declared:(the generic type parameter is the class being mapped, not the class map)
18 Posted by Adam Goss on 08 Apr, 2010 06:33 PM
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
Support Staff 19 Posted by James Gregory on 01 May, 2010 12:57 PM
FYI, there's now an
Extends
method inSubclassMap
which might help in situations where FNH can't figure out the hierarchy.James Gregory closed this discussion on 01 May, 2010 12:57 PM.