简体   繁体   中英

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?

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:

文件类型

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM