soupy1976
soupy1976

Reputation: 2845

Fluent NHibernate child class mapping using DiscriminateSubClassesOnColumn issue

I have an issue with mapping, simplified my relationship looks like this. I have parent class:

public abstract class DocumentType
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
}

and two subclasses:

public class UploadedFileDocument : DocumentType
{

}

public class ApplicationFormDocument : DocumentType
{
}

mapped like this:

public DocumentTypeMap()
{
    Schema("Core");
    Id(x => x.Id);
    Map(x => x.Name).Length(128).Not.Nullable();
    DiscriminateSubClassesOnColumn("Type");
}

public class UploadedFileDocumentMap : SubclassMap<UploadedFileDocument>
{

}

public class ApplicationFormDocumentMap : SubclassMap<ApplicationFormDocument>
{

}

Then I have another entity with a FK to DocumentType, mapped like this:

public FileConversionMap()
{
    Schema("Core");
    Id(x => x.Id);
    References(x => x.Application).Not.Nullable();
    References(x => x.DocumentType).Not.Nullable().Fetch.Select();           
}

my issue is, when I retrieve rows from the DB like this:

Session.Query<FileConversion>().AsQueryable();

all the rows come back with the DocumentType being of type DocumentType, not of the child type (ie the actual type of that property, ie. when i do .GetType(), either UploadedFileDocument or ApplicationFormDocument)

Apologies if this is just me being dim. But how can I determine which type of DocumentType I have ... is my mapping wrong?

Upvotes: 1

Views: 2497

Answers (1)

David R. Longnecker
David R. Longnecker

Reputation: 3157

When you look at your generated SQL (adding .ShowSQL() to your .Database method), do you see the Type being entered? You should see something similar to:

INSERT 
INTO
    "Core_DocumentType"
    (Name, Type) 
VALUES
    (@p0, 'ApplicationFormDocument');
select
    last_insert_rowid();
@p0 = 'afd' [Type: String (0)]

Using the mappings you provided, it looks fine and I could return the DocumentType (using SQLite) just fine.

Here's the code I used to reproduce it. I didn't have your FileConversion object, so please verify that it matches what you need.

DocumentType

public class DocumentType
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
}

public class DocumentTypeMap : ClassMap<DocumentType>
{
    public DocumentTypeMap()
    {
        GenerateMap();
    }

    void GenerateMap()
    {
        Schema("Core");
        Id(x => x.Id).GeneratedBy.Identity();
        Map(x => x.Name).Length(128).Not.Nullable();
        DiscriminateSubClassesOnColumn("Type");
    }
}

UploadFileDocument/ApplicationFormDocument

public class UploadedFileDocument : DocumentType
{
    public virtual string ContentType { get; set; }
}

public class ApplicationFormDocument : DocumentType
{
}

public class UploadFileDocumentMap : 
             SubclassMap<UploadedFileDocument>
{
    public UploadFileDocumentMap()
    {
        GenerateMap();
    }

    void GenerateMap()
    {
        Map(x => x.ContentType);
    }
}

public class ApplicationFormDocumentMap : 
             SubclassMap<ApplicationFormDocument>
{
}

FileConversion

public class FileConversion
{
    public virtual int Id { get; set; }
    public virtual DocumentType DocumentType { get; set; }
}

public class FileConversionMap : ClassMap<FileConversion>
{
    public FileConversionMap()
    {
        GenerateMap();
    }

    void GenerateMap()
    {
        Schema("Core");
        Id(x => x.Id).GeneratedBy.Identity();
        References(x => x.DocumentType).Not.Nullable().Fetch.Select();
    }
}

The tests I used (using machine.specifications):

Context

public class when_discriminating_on_subclass
{
   static IList<FileConversion> results;
   Establish context = () =>
   {
      using (var session = DataConfiguration.CreateSession())
      {
         using (var transaction = session.BeginTransaction())
         {
            var upload = new UploadedFileDocument 
                             { Name = "uploaded", ContentType = "test" };
            var form = new ApplicationFormDocument 
                             { Name = "afd" };
            session.Save(form);
            session.Save(upload);

            var formConversion = 
                new FileConversion { DocumentType = form };
            var uploadConversion = 
                new FileConversion { DocumentType = upload };
            session.Save(formConversion);
            session.Save(uploadConversion);

            transaction.Commit();
         }
         using (var transaction = session.BeginTransaction())
         {
            results = session.Query<FileConversion>().AsQueryable().ToList();
            transaction.Commit();
         }
     }
  };

Specifications

  It should_return_two_results = () =>
     results.Count.ShouldEqual(2);

  It should_contain_one_of_type_uploaded_file = () => 
     results
         .Count(x => x.DocumentType.GetType() == typeof(UploadedFileDocument))
         .ShouldEqual(1);

  It should_contain_one_of_type_application_form = () => 
     results
         .Count(x => x.DocumentType.GetType() == typeof(ApplicationFormDocument))
         .ShouldEqual(1);
}

Debugging through the assertions, I can see that the collection comes back with the two types:

DocumentType

Are you casting them back to the base type anywhere in your mappings or classes?

Upvotes: 1

Related Questions