Skip to content

Commit 23f67a3

Browse files
committed
Merge pull request #211 from tgmayfield/SubclassInterfaces
Subclass interfaces. Fixes NullReferenceExceptions caused by a null type.BaseType when the subclass type is an interface.
2 parents 4421b05 + d6dd56a commit 23f67a3

File tree

3 files changed

+167
-4
lines changed

3 files changed

+167
-4
lines changed

src/FluentNHibernate.Testing/PersistenceModelTests/SubclassPersistenceModelTests.cs

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,46 @@ public void ShouldHandleEverettsWeirdMapping()
203203
colorSource.Subclasses.ShouldContain(x => x.Type == typeof(Sauces.ReallyHotSauce));
204204
colorSource.Subclasses.Count().ShouldEqual(2);
205205
}
206+
207+
[Test]
208+
public void CanBuildConfigurationForTablePerType()
209+
{
210+
var model = new PersistenceModel();
211+
model.Add(new TablePerType.TPT_TopMap());
212+
model.Add(new TablePerType.TPT_TopSubclassMap());
213+
model.Add(new TablePerType.TPT_MiddleMap());
214+
model.Add(new TablePerType.TPT_MiddleSubclassMap());
215+
216+
var classMapping = model.BuildMappings();
217+
classMapping.Count().ShouldEqual(1);
218+
219+
var top = classMapping.First().Classes.First();
220+
top.Subclasses.Count().ShouldEqual(2);
221+
222+
var middle = top.Subclasses.SingleOrDefault(sc => sc.Type == typeof(TablePerType.TPT_Middle));
223+
middle.ShouldNotBeNull();
224+
middle.Subclasses.Count().ShouldEqual(1);
225+
}
226+
227+
[Test]
228+
public void CanBuildConfigurationForTablePerTypeWithInterfaces()
229+
{
230+
var model = new PersistenceModel();
231+
model.Add(new TablePerTypeWithInterfaces.TPTWI_ITopMap());
232+
model.Add(new TablePerTypeWithInterfaces.TPTWI_TopSubclassMap());
233+
model.Add(new TablePerTypeWithInterfaces.TPTWI_IMiddleMap());
234+
model.Add(new TablePerTypeWithInterfaces.TPTWI_MiddleSubclassMap());
235+
236+
var classMapping = model.BuildMappings();
237+
classMapping.Count().ShouldEqual(1);
238+
239+
var top = classMapping.First().Classes.First();
240+
top.Subclasses.Count().ShouldEqual(2);
241+
242+
var middle = top.Subclasses.SingleOrDefault(sc => sc.Type == typeof(TablePerTypeWithInterfaces.TPTWI_IMiddle));
243+
middle.ShouldNotBeNull();
244+
middle.Subclasses.Count().ShouldEqual(1);
245+
}
206246
}
207247

208248
namespace Thoughts
@@ -365,6 +405,124 @@ public class I_BottomMostMap : SubclassMap<I_BottomMost>
365405
{ }
366406
}
367407

408+
namespace TablePerType
409+
{
410+
public class TPT_Top
411+
{
412+
public virtual int Id { get; protected set; }
413+
}
414+
public class TPT_Middle
415+
: TPT_Top
416+
{
417+
public virtual string MiddleProperty { get; set; }
418+
}
419+
public class TPT_TopSubclass
420+
: TPT_Top
421+
{
422+
}
423+
public class TPT_MiddleSubclass
424+
: TPT_Middle
425+
{
426+
public virtual string OwnProperty { get; set; }
427+
}
428+
429+
public class TPT_TopMap
430+
: ClassMap<TPT_Top>
431+
{
432+
public TPT_TopMap()
433+
{
434+
Id(x => x.Id);
435+
}
436+
}
437+
public class TPT_TopSubclassMap
438+
: SubclassMap<TPT_TopSubclass>
439+
{
440+
public TPT_TopSubclassMap()
441+
{
442+
KeyColumn("Id");
443+
}
444+
}
445+
public class TPT_MiddleMap
446+
: SubclassMap<TPT_Middle>
447+
{
448+
public TPT_MiddleMap()
449+
{
450+
KeyColumn("Id");
451+
Map(x => x.MiddleProperty);
452+
}
453+
}
454+
public class TPT_MiddleSubclassMap
455+
: SubclassMap<TPT_MiddleSubclass>
456+
{
457+
public TPT_MiddleSubclassMap()
458+
{
459+
KeyColumn("Id");
460+
Map(x => x.OwnProperty);
461+
}
462+
}
463+
}
464+
465+
namespace TablePerTypeWithInterfaces
466+
{
467+
public interface TPTWI_ITop
468+
{
469+
int Id { get; }
470+
}
471+
public interface TPTWI_IMiddle
472+
: TPTWI_ITop
473+
{
474+
string MiddleProperty { get; set; }
475+
}
476+
477+
public class TPTWI_TopSubclass
478+
: TPTWI_ITop
479+
{
480+
public virtual int Id { get; protected set; }
481+
}
482+
public class TPTWI_MiddleSubclass
483+
: TPTWI_TopSubclass, TPTWI_IMiddle
484+
{
485+
public virtual string MiddleProperty { get; set; }
486+
public virtual string OwnProperty { get; set; }
487+
}
488+
489+
public class TPTWI_ITopMap
490+
: ClassMap<TPTWI_ITop>
491+
{
492+
public TPTWI_ITopMap()
493+
{
494+
Id(x => x.Id);
495+
}
496+
}
497+
public class TPTWI_TopSubclassMap
498+
: SubclassMap<TPTWI_TopSubclass>
499+
{
500+
public TPTWI_TopSubclassMap()
501+
{
502+
Extends<TPTWI_ITop>();
503+
KeyColumn("Id");
504+
}
505+
}
506+
public class TPTWI_IMiddleMap
507+
: SubclassMap<TPTWI_IMiddle>
508+
{
509+
public TPTWI_IMiddleMap()
510+
{
511+
KeyColumn("Id");
512+
Map(x => x.MiddleProperty);
513+
}
514+
}
515+
public class TPTWI_MiddleSubclassMap
516+
: SubclassMap<TPTWI_MiddleSubclass>
517+
{
518+
public TPTWI_MiddleSubclassMap()
519+
{
520+
KeyColumn("Id");
521+
Map(x => x.OwnProperty);
522+
}
523+
}
524+
}
525+
368526
namespace Branching
369527
{
370528
public class B_Top

src/FluentNHibernate/Mapping/SubclassMap.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -292,11 +292,16 @@ SubclassMapping IIndeterminateSubclassMappingProvider.GetSubclassMapping(Subclas
292292
attributes.Set("DiscriminatorValue", Layer.Defaults, typeof(T).Name);
293293

294294
// TODO: un-hardcode this
295-
var key = new KeyMapping();
296-
key.AddColumn(Layer.Defaults, new ColumnMapping(typeof(T).BaseType.Name + "_id"));
295+
Type baseType = typeof(T).BaseType
296+
?? attributes.Get("Extends") as Type;
297+
if (baseType != null)
298+
{
299+
var key = new KeyMapping();
300+
key.AddColumn(Layer.Defaults, new ColumnMapping(baseType.Name + "_id"));
301+
attributes.Set("Key", Layer.Defaults, key);
302+
}
297303

298304
attributes.Set("TableName", Layer.Defaults, GetDefaultTableName());
299-
attributes.Set("Key", Layer.Defaults, key);
300305

301306
// TODO: this is nasty, we should find a better way
302307
mapping.OverrideAttributes(attributes.Clone());

src/FluentNHibernate/Visitors/SeparateSubclassVisitor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ private IDictionary<int, IList<IIndeterminateSubclassMappingProvider>> SortByDis
9191
var subclassType = subclassProvider.EntityType;
9292
var level = 0;
9393

94-
bool implOfParent = parentType.IsInterface
94+
bool implOfParent = (parentType.IsInterface || subclassType.IsInterface)
9595
? DistanceFromParentInterface(parentType, subclassType, ref level)
9696
: DistanceFromParentBase(parentType, subclassType.BaseType, ref level);
9797

0 commit comments

Comments
 (0)