1
votes

Last week I received the assignment to look for an alternative to the NHibernate hbm mapping, and decided to give the NHibernate mapping-by-code a try (seen that the fluent NHibernate project hasn't been updated in the last couple of years). Thank to NOtherDev blog and a lot of other questions here on SO, I was able to map almost everything, but I still have a couple of issues.

Issue: I have an entity that as a Component of type IDocument, and it has a private implementation of this interface, but when I try to map the various properties of this component I get the error Could not find property nor field XXX, because in the interface this properties do not have the setter.

Interface

public interface IDocument : IObject
{
    byte[] DocumentData { get; }
    string Name { get; }
    DocumentFormats Format { get; }
}

Entity class (to be mapped)

public class ArchiveDocument : /* [...] */, IArchiveDocument
{
    private readonly MyDocument _document = new MyDocument();
    public IDocument Document
    {
        get { return _document; }
        set
        {
            ThrowExceptionIfReadonly(nameof(Document));

            _document.Format = value.Format;
            _document.Name = value.Name;
            _document.DocumentData = value.DocumentData;
        }
    }
    // private document implementation
    private class MyDocument : IDocument
    {
        public MyDocument(){ /* constructor */}
        private byte[] _documentData;
        public byte[] DocumentData
        {
            get { return _documentData; }
            set
            {
                _documentData = value;
            }
        }

        private string _name;
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        private DocumentFormats _format;
        public DocumentFormats Format
        {
            get { return _format; }
            set { _format = value; }
        }
    }
}

Mapping

public class ArchiveDocumentMap : ClassMapping<ArchiveDocument>
{
    Table("DOCUMENT");
    Lazy(false);
    OptimisticLock(OptimisticLockMode.Version);

    Component(d => d.Document, d =>
    {
        d.Access(Accessor.Field);
        d.Property(dd => dd.DocumentData, dd =>
        {
            dd.Column("DOC_B_DOCUMENT_DATA");
            dd.Access(Accessor.Field);
            dd.Type<BinaryBlobType>();
            dd.Insert(true);
            dd.Update(false);
        });
        d.Property(dd => dd.Format, dd =>
        {
            dd.Column("DOC_S_FORMAT");
            dd.Access(Accessor.Field);
            dd.Type<EnumStringType<DocumentFormats>>();
        });
        d.Property(dd => dd.Name, dd =>
        {
            dd.Column("DOC_S_NAME");
            dd.Access(Accessor.Field);
            dd.Type</*own type*/>();
         });
    });
}

old hbm

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="/*namespace of class*/" assembly="/*namespace of assebly*/.ArchiveDocument">
  <class name="ArchiveDocument" table="DOCUMENT" optimistic-lock="version" lazy="false">
    <component name="Document" access="field.camelcase-underscore">
      <property name="DocumentData" column="DOC_B_DOCUMENT_DATA" access="field.camelcase-underscore" type="BinaryBlob" insert="true" update="false"/>
      <property name="Format" column="DOC_S_FORMAT" access="field.camelcase-underscore" type="NHibernate.Type.EnumStringType`1[[/* Enum path */]], NHibernate"/>
      <property name="Name" column="DOC_S_NAME" access="field.camelcase-underscore" type="/* Own type (variant of string) */"/>
    </component>
  </class>
</hibernate-mapping>

Error

Could not find property nor field 'DocumentData' in class '/* Namespace */.IDocument'

From what I understand both, mapping-by-code and Fluent NHibernate, are not "interface friendly", but in my opinion if the hbm file was able to map this class, so should mapping-by-code. Am I doing something wrong, or is there no way to do what I want with mapping-by-code? If the latter, is there a work around?

Edit:

The error is not DocumentData specific, If I comment it out, the same happens for Name and Format.

Edit 2: Complete Stacktrace of inner exception

[PropertyNotFoundException: Could not find property nor field 'DocumentData' in class 'Eis.Framework.Business.Core.Document.IDocument']
   NHibernate.Properties.FieldAccessor.GetField(Type type, String fieldName, Type originalType) +220
   NHibernate.Properties.FieldAccessor.GetField(Type type, String fieldName, Type originalType) +90
   NHibernate.Properties.FieldAccessor.GetGetter(Type theClass, String propertyName) +84
   NHibernate.Tuple.Component.PocoComponentTuplizer.BuildGetter(Component component, Property prop) +41
   NHibernate.Tuple.Component.AbstractComponentTuplizer..ctor(Component component) +168
   NHibernate.Tuple.Component.PocoComponentTuplizer..ctor(Component component) +21
   NHibernate.Tuple.Component.ComponentEntityModeToTuplizerMapping..ctor(Component component) +368
   NHibernate.Tuple.Component.ComponentMetamodel..ctor(Component component) +301
   NHibernate.Mapping.Component.BuildType() +28
   NHibernate.Mapping.Component.get_Type() +16
   NHibernate.Mapping.SimpleValue.IsValid(IMapping mapping) +30
   NHibernate.Mapping.PersistentClass.Validate(IMapping mapping) +87
   NHibernate.Mapping.RootClass.Validate(IMapping mapping) +35
   NHibernate.Cfg.Configuration.ValidateEntities() +169
   NHibernate.Cfg.Configuration.Validate() +13
   NHibernate.Cfg.Configuration.BuildSessionFactory() +34
   [namespace].UnitOfWorkProviderNHibernate.InitializeImpl(IDictionary`2 parameters) in C:\path\to\file\UnitOfWorkProviderNHibernate.cs:241
   [namespace].Initialize(IDictionary`2 parameters) in C:\path\to\file\ProviderBase.cs:36
   [namespace].InitializeProvider(ProviderItem providerItem) in C:\path\to\file\BusinessProviderFactory.cs:287

Edit 3:

I tried to see what the generated hbm.xml file looks like to compare it with the one we are using now, that I know it works, and this are my findings:

  • In the component tag the class attribute is filled in (even though I didn't specify it in my Map class)
  • The accessors are field and not field.camelcase-underscore
  • The attribute insert=true is missing in DocumentData (it is explicitly given in my Map class)

1

1 Answers

0
votes

Century! I think, the main problem here: d.Property(dd => dd.DocumentData As I can see, this class is private, and unfortunately, NHibernate can't to map it, and create the objects. Please, change the access modifiers to this object.