No persister for… {SUBCLASS} NHibernate with Fluent NHibernate

fluent-nhibernatenhibernate

How do I let NHibernate ignore extra properties of a subclass of my model?

class SuperModel { // hot I know
{
    public Guid Id { get; private set; }
    public string FirstName { get; set; }
}

class SubModel : SuperModel {
    public string FavoriteColor { get; set; }
}

I really only want to store the SuperModel data using my repository and use the FavoriteColor elsewhere, but I get

No persister for: SubModel

even though I save it with my repository as

void Store(SuperModel model) {
   using (var session = Session){
       session.SaveOrUpdate(model); // <<<< The exception is thrown here
   }
}

and some where else I use

void WhatToDo(SubModel model) {
   doSomething(model.FavoriteColor);
}

And I use it as such

var model = new SubModel { FirstName = "Miranda", FavoriteColor = "Green" };
modelRepository.Store(model);
someService.WhatToDo(model);

Any one know how I can fluently configure this? Thanks.

FYI- implicit and explicit casting has no effect.

Edit

My mappings are like this

class SuperModelMap : ClassMap<SuperModel>
{
    public SuperModelMap()
    {
        WithTable("SuperModels");
        Id(x => x.Id);
        Map(x => x.FirstName);
    }
}

Edit 2

I figure/found out that I could do this, but in my database, I have to have a dummy table, which would just be inefficient. It works but there has to be a better way…

In my SuperModelMap…

JoinedSubClass<SubModel>("SubModel", MapSubModel);

private void MapSubModel(JoinedSubClassPart<SubModel> part)
{
    // Leave this empty
}

Edit 3
I'm closer, but I still get a different error on selection.

I tried this.

DiscriminateSubClassesOnColumn("Id")
    .SubClass<SubModel>(m => { });

InnerException {"Object with id:
5586b075-47f1-49c8-871c-9c4d013f7220
was not of the specified subclass:
SuperUser (Discriminator was:
'1000')"} System.Exception
{NHibernate.WrongClassException}

Best Answer

You can refine this solution to make it more reusable. As I understand, you don't like mapping duplication. This can be avoided:

I have created a SuperModelMapHelper class that contains an extension method:

public static class SuperModelMapHelper
{
    public static void MapSuperModel<T>(this ClassMap<T> classMap)
        where T : SuperModel
    {
        classMap.WithTable("SuperModels");
        classMap.Id(x => x.Id);
        classMap.Map(x => x.FirstName);
    }
}

As you can see - it's generic and will accept any of SuperModel's subclasses. Then, there are two mappings:

public class SuperModelMap : ClassMap<SuperModel>
{
    public SuperModelMap()
    {
        MapSuperModel();
    }
}

public class SubModelMap : ClassMap<SubModel>
{
    public SubModelMap()
    {
        MapSuperModel();
    }
}

I've used extension method to preserve convention of FluentNHibernate, you can make it simple static method and pass class map as a parameter.

And this code:

Guid id;
using (var session = sf.OpenSession())
using (var transaction = session.BeginTransaction())
{
    var subModel = new SubModel()
                        {FavoriteColor = "blue", FirstName = "Jane"};
    session.Save(subModel);
    id = subModel.Id;
    transaction.Commit();
}

using (var session = sf.OpenSession())
using (var transaction = session.BeginTransaction())
{
    var superModel = session.Get<SuperModel>(id);
    Console.WriteLine(superModel.GetType().Name);
    Console.WriteLine(superModel.FirstName);
    transaction.Commit();
}

Works as intended - type is SuperClass. Note that I've created second session. You'd have to flush your session before trying to load entity in the same session you saved it, because NHibernate defers query execution.

Using this solution there is very little duplication. You can investigate AutoMapping feature of FluentNHibernate to reduce it even more - perhaps creating own convention would let you to automatically map such classes.