nhibernate / nhibernate-core

NHibernate Object Relational Mapper
https://nhibernate.info
GNU Lesser General Public License v2.1
2.13k stars 925 forks source link

Can not use joined-subclass of xml mapping to extend the mappings build with NHibernate.Mapping.ByCode #2307

Open beginor opened 4 years ago

beginor commented 4 years ago

I am refacting the mapping of NHibernate.AspNetCore.Identity to NHibernate.Mapping.ByCode , as descripted in the issue https://github.com/nhibernate/NHibernate.AspNetCore.Identity/issues/16 .

I have rewrite the orignal xml mapping of NHibernate.AspNetCore.Identity with ClassMapping<T>, the code looks like this:

public class IdentityRoleMappingPostgreSql : ClassMapping<IdentityRole> {

    public IdentityRoleMappingPostgreSql() {
        Schema("public");
        Table("aspnet_roles");
        Id(e => e.Id, id => {
            id.Column("id");
            id.Type(NHibernateUtil.String);
            id.Length(32);
            id.Generator(Generators.TriggerIdentity);
        });
        Property(e => e.Name, prop => {
            prop.Column("name");
            prop.Type(NHibernateUtil.String);
            prop.Length(64);
            prop.NotNullable(true);
            prop.Unique(true);
        });
        /* other property mappings ignored here .*/
    }

}

The full mapping code is here:

https://github.com/nhibernate/NHibernate.AspNetCore.Identity/tree/master/src/NHibernate.AspNetCore.Identity/Entities

Then I try to extend these mappings with joined-sublcass of xml mapping, like this:

<?xml version="1.0" encoding="UTF-8" ?>
<hibernate-mapping
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns="urn:nhibernate-mapping-2.2"
  namespace="WebTest.Entities" assembly="WebTest">

  <joined-subclass name="AppRole" schema="public" table="app_roles" extends="NHibernate.AspNetCore.Identity.IdentityRole, NHibernate.AspNetCore.Identity, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
    <key column="id" />
    <property name="Description" column="description" type="string" length="256" />
  </joined-subclass>

</hibernate-mapping>

I got the following exception when setting up nhibernate:

NHibernate.MappingException : These classes referenced by 'extends' were not found:
FullName:NHibernate.AspNetCore.Identity.IdentityRole - Name:NHibernate.AspNetCore.Identity.IdentityRole, NHibernate.AspNetCore.Identity, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
FullName:NHibernate.AspNetCore.Identity.IdentityUser - Name:NHibernate.AspNetCore.Identity.IdentityUser, NHibernate.AspNetCore.Identity, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
   at NHibernate.Cfg.MappingsQueue.CheckNoUnavailableEntries()
   at NHibernate.Cfg.Configuration.SecondPassCompile()
   at NHibernate.Cfg.Configuration.BuildMappings()
   at UnitTest.IdentityTest._02_CanSetupMappingByXml()

Then I rewrite the xml mapping with JoinedSubclassMapping<AppRole> , which works , can build session factory and query without any exception.

public class AppRoleMapping : JoinedSubclassMapping<AppRole> {

    public AppRoleMapping() {
        ExplicitDeclarationsHolder
            .AddAsRootEntity(typeof(NHIdentityRole));
        Extends(typeof(NHIdentityRole));
        Schema("public");
        Table("app_roles");
        Key(k => k.Column("id"));
        Property(
            p => p.Description,
            maping => {
                maping.Column("description");
                maping.Type(NHibernateUtil.String);
                maping.Length(256);
            }
        );
    }

}

So the issue is:

The full test code is here: https://github.com/nhibernate/NHibernate.AspNetCore.Identity/blob/master/test/UnitTest/IdentityTest.cs

Maybe there something I do wrong, or some issues with nhibernate's mappings?

beginor commented 4 years ago

After debug and check the source code of NHibernate.Cfg.Configuration, there is an internal validation queue to process the mappings added by xml, but no way to skip it. So the simple work around is converting the HbmMapping to xml, then add the xml to configuration, let's double check it.

var mapper = new ModelMapper();
mapper.AddMapping<IdentityRoleMappingPostgreSql>();
// add other mapping here.
var mapping = mapper.CompileMappingForAllExplicitlyAddedEntities();
// Use AddXml instead of AddMappings
cfg.AddXml(mapping.AsString());
fredericDelaporte commented 4 years ago

I do not know if mixing hbm and By-Code mappings is an "accidental" feature or if it is supposed to work flawlessly in any cases. So I do not really know if the trouble your report should be qualified as a bug or as a feature request.

For now I settle on feature request.

beginor commented 4 years ago

I think it should be a feature too. Maybe we should provide an optional parameter for mapping by xml, let users to specify explicited root entity, provide similar function like the ExplicitDeclarationsHolder.AddAsRootEntity() method in mapping by code.

craigfowler commented 4 years ago

I've just run into this and I can provide a very good (at least IMO) reason why this scenario should be supported.

That's because of issue #1277, for which the workaround is essentially "Use an .hbm.xml mapping instead of mapping-by-code for the affected class". This has meant that I have needed to mix-and-match MbC class mappings and a single XML embedded resource mapping (which just happened to be for a <subclass />) in the same environment.

FWIW, the workaround @beginor described (convert the MbC mappings to XML & import as XML, then add .hbm.xml mappings) did work for me.

craigfowler commented 2 weeks ago

I have noticed that the workaround: using MBC, then convert those mappings to XML and add them that way, is somewhat less viable from NHibernate 5.4.1 onward. That's if those mappings contain any one-to-one relationships, because of a new crash bug adding the mappings. See #3607 for more information.