I’ve just committed a breaking change to Fluent NHibernate (as of r184), which I thought I’d document here for anyone interested; it’s a reworking of the subclass syntax.

Mapping multiple subclasses with the same parent wasn’t a very fluent affair, and it was pretty wordy too. You can see a comparison of the old and new syntaxes below.

Before

var discriminator = DiscriminateSubClassesOnColumn<string>("Type");

discriminator.SubClass<B>()
  .IdentifiedBy("bID")
  .MapSubClassColumns(m =>
  {
    m.Map(x => x.BProperty);
  });

discriminator.SubClass<C>()
  .IdentifiedBy("cID")
  .MapSubClassColumns(m =>
  {
    m.Map(x => x.CProperty);
  });

After

DiscriminateSubClassesOnColumn("Type")
  .SubClass<B>(m =>
  {
    m.Map(x => x.BProperty);
  })
  .SubClass<C>(m =>
  {
    m.Map(x => x.CProperty);
  });

Much nicer! The changes you can see here are:

  • DiscriminateSubClassesOnColumn now assumes your discriminator is a string if you don’t specify a type
  • SubClass defaults to using the subclass type name as a discriminator value
  • IdentifiedBy and MapSubClassColumns are now merged into SubClass as overloads.

Nested subclasses were never really supported in Fluent NHibernate, but they were hackable. You could abuse DiscriminateSubClassesOnColumn to let you trick it into creating nested classes. This worked but it led to some really ugly mapping code (and a nasty hack in the Fluent NHibernate codebase). I’ve given some loving to this area and have managed to really sweeten-up the syntax.

Before

DiscriminateSubClassesOnColumn<string>("Type")
  .SubClass<B>()
    .IdentifiedBy("bID")
    .MapSubClassColumns(m =>
    {
      m.Map(x => x.BProperty);
      m.DiscriminateSubClassesOnColumn<string>("Type")
        .SubClass<C>()
          .IdentifiedBy("cID")
          .MapSubClassColumns(m =>
          {
            m.Map(x => x.CProperty);
          });
    });

After

DiscriminateSubClassesOnColumn("Type")
  .SubClass<B>(m =>
  {
    m.Map(x => x.BProperty);
    m.SubClass<C>(m =>
    {
      m.Map(x => x.CProperty);
    });
  });

The changes in this one are:

  • SubClass can now be used within subclasses without having to reuse DiscriminateSubClassesOnColumn

All in all, these changes serve to make mapping subclasses in Fluent NHibernate a little bit neater.

Update

As requested, here are the domain entites that the above mappings represent.

Two subclasses with shared parent

public class A
{}

public class B : A
{
  public virtual string BProperty { get; set; }
}

public class C : A
{
  public virtual string CProperty { get; set; }
}

Subclass of a subclass

public class A
{}

public class B : A
{
  public virtual string BProperty { get; set; }
}

public class C : B
{
  public virtual string CProperty { get; set; }
}
Tags:

Comments...

  1. Very nice changes James. You’ve been a whirlwind of Fluent NHibernate activity this last week or so. You are doing a stellar job.

    By Paul Batum1 Jan, 2009 @ 12:38 am

  2. Could you post what your class objects would actually look like for these mappings?

    By Chris Marisic1 Jan, 2009 @ 4:46 am

  3. @Paul: Thanks Paul, I put Christmas to good use for once ;)

    @Chris: I’ve updated the post with the domain entities, but they’re pretty simple really.

    By James Gregory1 Jan, 2009 @ 12:43 pm

  4. Hi,
    there is a little bug, if you try to add a version column to the ClassMap, the tag in the xml file is added before the discriminator tag and therefore raising an exception (the discriminator tag has to be directly below the id tag)

    By Harald Croll — 1 Jan, 2009 @ 10:52 pm

  5. @Harold: Thanks for that, I’ll create an issue for it and we’ll tackle it as soon as we can.

    By James Gregory1 Jan, 2009 @ 10:55 pm

  6. Great work. I am really loving using this.
    I noticed a bug where .DiscriminateSubClassesOnColumn(“ColumnName”) still tried to use a string type for the column. It does work with a default as .DiscriminateSubClassesOnColumn(“NetworkTypeID”, 0).
    I’ve also been working on some enhancements to the test framework. When I get them working I’ll pass them on.

    By Ian Shimmings — 1 Jan, 2009 @ 5:55 pm

  7. Oops. With encoding included.
    I noticed a bug where .DiscriminateSubClassesOnColumn<int>(”ColumnName”) still tried to use a string type for the column. It does work with a default as .DiscriminateSubClassesOnColumn(”NetworkTypeID”, 0).

    By Ian Shimmings — 1 Jan, 2009 @ 5:57 pm

  8. public MapSample() {
      Id(x => x.Id);
      //stringa da un type
      DiscriminateSubClassesOnColumn("Type")
        .SubClass<B>(m =>{
          m.Map(x => x.BProperty);
        })
        .SubClass<C>(m =>{
          m.Map(x => x.CProperty);
        });
    }
    
    public class A :entity.Entity /*Entity holds the Id */
    {}  
    
    public class B : A
    {
      public virtual string BProperty { get; set; }
    }  
    
    public class C : A
    {
      public virtual string CProperty { get; set; }
    }

    This little Fixture is required -pun intended. Admittedly it’s obvious, yet… Thank you, I like it.

    By Guido — 2 Feb, 2009 @ 8:26 am

  9. Interessante Informationen.

    By lieben3 Mar, 2009 @ 3:27 am

  10. I’m trying to work out the following scenario with regards to Fluent NHibernate notation for the following:

    public class A { }

    public class B : A { }

    public class C : B { }

    public class D : B { }

    Where the persisted data for class B, class C and class D are all stored in different tables to A.

    If the discriminator for type C and D is stored in table B and is a simple char field, what notation is required to:

    1) Allow for a 1-to-1 relationship between the parent and subtype record in table B and table C
    2) Allow for a 1-to-1 relationship between the parent and subtype record in table B and table D?

    By kiwisurfer — 3 Mar, 2009 @ 4:17 pm

  11. How do you do the same with .hbm files? meaning, 1 .hbm file per sub class (can ignore the super class) ? Its like, all 3 classes mapped to 1 table, but in 2 hbm files? (hope you know what i meant).

    By kosalanp3 Mar, 2009 @ 7:13 pm

  12. Sorry but your comment got stuck in limbo for a bit there. I’d suggest you hit the mailing list for support as you’re much more likely to get a response.

    As for your particular question, I’m afraid you’re out of luck right now. We don’t currently support that scenario, but we are very much aware of a need for it.

    By James Gregory3 Mar, 2009 @ 5:00 pm

  13. How can I to create a mapping when a Person can be Employee and/or Customer and/or Supplier, using FNH?

    By Luiz Sergio — 4 Apr, 2009 @ 6:10 pm

  14. Please ask on the Fluent NHibernate mailing list.

    By James Gregory4 Apr, 2009 @ 11:34 am

  15. Hi James,

    When using this approach, only table A is created for all given types.

    How can I have a table per subtype using this synthax?

    By Dragos Haiduc — 5 May, 2009 @ 10:56 am

  16. This has been deprecated. Currently, to do this you can create another mapping that inherits from SubclassMap.

    Just posting in case someone stumbles on this like I did.

    http://wiki.fluentnhibernate.org/Fluent_mapping#Subclasses for more information.

    By Rex Morgan7 Jul, 2010 @ 9:23 pm

Post a comment...

Trackbacks...

  1. Pingback from links for 2009-07-22 « Praveen’s Blog