0
votes

EDIT: Added HBM mapping at the end of the post.

I have a rather large class library, with inheritance involved. I'm using Fluent NHibernate to map the classes to MS SQL 2008 to host this in Azure.

I'm fairly sure the mappings are correct. We use a Discriminator Value to differentiate the classes on column (not on table). However, whenever I try to generate the mappings, I get an error that one of the class/entity is a duplicate.

Duplicate class/entity mapping FolkeLib.MMOBash.Bash

This happens here:

private static ISessionFactory CreateSessionFactory()
    {
        return Fluently.Configure()
            .Database(
                MsSqlConfiguration.MsSql2008.Dialect("NHibernate.Dialect.MsSql2008Dialect")
                .ConnectionString(c => 
                    c.FromConnectionStringWithKey("FolkeConnString"))
                .ShowSql())
            .Mappings(m => 
                m.FluentMappings.AddFromAssemblyOf<Account>())
            .ExposeConfiguration(cfg => 
                new SchemaUpdate(cfg).Execute(false, true))
            .Diagnostics(diag => diag.Enable().OutputToConsole())
            .BuildSessionFactory();
    }

The class hierarchy is something like this

  • Message
    • Comment
    • ForumMessage
      • Article
        • Image
          • Bash

Bash inherits from Image, which Inherits from Article, and so on. They all have a different discriminator value. Message mappings are defined like this:

public class MessageMap : ClassMap<Message>
{
    MessageMap()
    {
        Id(x => x.Id);
        DiscriminateSubClassesOnColumn("MessageType").SqlType("int32");
        Map(x => x.CreationDate).Index("ArticleCreationDate");
        Map(x => x.ModificationDate);
        Map(x => x.LastChildCreationDate);
        References(x => x.Author);
        References(x => x.RootMessage).Nullable(); ;
        References(x => x.ParentMessage).Nullable(); ;
    }
}

(there are more properties but they are irrelevant I think). Note that RootMessage and ParentMessages are themselves of the type "Message." Could this be the root cause?

Bash mappings are like this:

public class BashMap : SubclassMap<Bash>
{
    BashMap()
    {
        DiscriminatorValue(5);
        Map(x => x.Game);
        Map(x => x.Language);
        References(x => x.ApprovedBy);
    }
}

I have spent hours on this and I have no idea why I get a Duplicate class/entity mapping error.

EDIT: Troubleshooting update.

Following suggestions below I added this bit of code:

public ActionResult Zogzog()
    {
        List<string> types = new List<string>();
        foreach (var module in typeof(Account).Assembly.GetModules())
        {
            foreach (var type in module.GetTypes())
            {
                if (typeof(IMappingProvider).IsAssignableFrom(type))
                {
                    types.Add(type.ToString());
                }                    
            }   
        }
        ViewBag.Types = types;
        return View();
    }

The output of the view is this:

FolkeLib.Calendar.EventRoleMap

FolkeLib.Calendar.LocationMap

FolkeLib.Calendar.RegistrationMap

FolkeLib.Calendar.RoleMap

FolkeLib.Domain.AccountMap

FolkeLib.Domain.AccountBanMap

FolkeLib.Domain.CommunityMap

FolkeLib.Domain.ContactEntryMap

FolkeLib.Domain.ContactTypeMap

FolkeLib.Domain.ForumMap

FolkeLib.Domain.ReadMessageMap

FolkeLib.Domain.RssFeedMap

FolkeLib.Domain.SkinMap

FolkeLib.Domain.GroupMap

FolkeLib.Domain.LanguageMap

FolkeLib.Domain.MenuMap

FolkeLib.Domain.MenuItemMap

FolkeLib.Domain.MessageMap

FolkeLib.Domain.PollMap

FolkeLib.Domain.PollAnswerMap

FolkeLib.Domain.PollVoteMap

FolkeLib.Domain.QuoteMap

FolkeLib.Domain.MessageReportMap

FolkeLib.Game.CharacterMap

FolkeLib.Domain.RssChannelMap

FolkeLib.Domain.SiteMap

FolkeLib.Domain.SiteApplicationModuleMap

FolkeLib.Domain.StaticTextMap

FolkeLib.Domain.TagMap

FolkeLib.Domain.VoteMap

I don't seem to have duplicates, however none of the children of Message seem to appear. I'm not sure if that is normal (they are of type SubclassMap, what is shown here is of type ClassMap) or not but this was asked in the comments.

EDIT: This is the Message.hbm.xml file generated by Fluent. As we can see, some classes (Bash, Article, ...) are shown several time as subclasses, sometimes with different discriminator values!

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <class xmlns="urn:nhibernate-mapping-2.2" name="FolkeLib.Domain.Message, FolkeLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="`Message`">
    <id name="Id" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Id" />
      <generator class="identity" />
    </id>
    <discriminator type="String">
      <column name="MessageType" sql-type="int32" />
    </discriminator>
    <property name="CreationDate" type="System.DateTime, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="CreationDate" index="ArticleCreationDate" />
    </property>
    <property name="ModificationDate" type="System.DateTime, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="ModificationDate" />
    </property>
    <property name="LastChildCreationDate" type="System.DateTime, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="LastChildCreationDate" />
    </property>
    <property name="Text" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Text" />
    </property>
    <property name="Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Locked" />
    </property>
    <property name="Score" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Score" />
    </property>
    <property name="Hidden" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Hidden" />
    </property>
    <property name="Deleted" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Deleted" />
    </property>
    <property name="AuthorIp" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="AuthorIp" />
    </property>
    <property name="PublicationDate" type="System.DateTime, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="PublicationDate" />
    </property>
    <many-to-one class="FolkeLib.Domain.Account, FolkeLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="Author">
      <column name="Author_id" />
    </many-to-one>
    <many-to-one class="FolkeLib.Domain.Account, FolkeLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="Locker">
      <column name="Locker_id" not-null="false" />
    </many-to-one>
    <many-to-one class="FolkeLib.Domain.Message, FolkeLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="RootMessage">
      <column name="RootMessage_id" not-null="false" />
    </many-to-one>
    <many-to-one class="FolkeLib.Domain.Message, FolkeLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="ParentMessage">
      <column name="ParentMessage_id" not-null="false" />
    </many-to-one>
    <many-to-one class="FolkeLib.Domain.Site, FolkeLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="Site">
      <column name="Site_id" not-null="true" />
    </many-to-one>
    <many-to-one class="FolkeLib.Domain.Forum, FolkeLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="Forum">
      <column name="Forum_id" />
    </many-to-one>
    <many-to-one class="FolkeLib.Domain.Account, FolkeLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="DeletedBy">
      <column name="DeletedBy_id" />
    </many-to-one>
    <subclass name="FolkeLib.Domain.Comment, FolkeLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" discriminator-value="2" />
    <subclass name="FolkeLib.Domain.ForumMessage, FolkeLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" discriminator-value="3">
      <property name="Title" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
        <column name="Title" />
      </property>
      <subclass name="FolkeLib.Calendar.Event, FolkeLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" discriminator-value="6">
        <bag lazy="true" name="RoleSet" table="EventEventRole">
          <key>
            <column name="Event_id" />
          </key>
          <many-to-many class="FolkeLib.Calendar.EventRole, FolkeLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
            <column name="EventRole_id" />
          </many-to-many>
        </bag>
        <property name="Duration" type="System.TimeSpan, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
          <column name="Duration" />
        </property>
        <property name="RecurringDays" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
          <column name="RecurringDays" />
        </property>
        <property name="FirstTime" type="System.DateTime, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
          <column name="FirstTime" />
        </property>
        <property name="LastTime" type="System.DateTime, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
          <column name="LastTime" />
        </property>
        <many-to-one class="FolkeLib.Calendar.Location, FolkeLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="Location">
          <column name="Location_id" />
        </many-to-one>
      </subclass>
      <subclass name="FolkeLib.Domain.Article, FolkeLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" discriminator-value="1">
        <subclass name="FolkeLib.Domain.Image, FolkeLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" discriminator-value="1">
          <property name="Name" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
            <column name="Name" />
          </property>
          <property name="Extension" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
            <column name="Extension" />
          </property>
          <property name="Public" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
            <column name="Public" />
          </property>
          <property name="ForAdults" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
            <column name="ForAdults" />
          </property>
          <subclass name="FolkeLib.MMOBash.Bash, FolkeLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" discriminator-value="5">
            <property name="Game" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
              <column name="Game" />
            </property>
            <property name="Language" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
              <column name="Language" />
            </property>
            <many-to-one class="FolkeLib.Domain.Account, FolkeLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="ApprovedBy">
              <column name="ApprovedBy_id" />
            </many-to-one>
          </subclass>
        </subclass>
      </subclass>
      <subclass name="FolkeLib.Domain.Article, FolkeLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" discriminator-value="4">
        <bag name="TagSet" table="TagToArticle">
          <key>
            <column name="Article_id" />
          </key>
          <many-to-many class="FolkeLib.Domain.Tag, FolkeLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
            <column name="Tag_id" />
          </many-to-many>
        </bag>
        <bag name="ImageSet" table="ImageToArticle">
          <key>
            <column name="Article_id" />
          </key>
          <many-to-many class="FolkeLib.Domain.Image, FolkeLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
            <column name="Image_id" />
          </many-to-many>
        </bag>
        <property name="ExtraText" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
          <column name="ExtraText" />
        </property>
        <subclass name="FolkeLib.Domain.Image, FolkeLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" discriminator-value="1">
          <property name="Name" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
            <column name="Name" />
          </property>
          <property name="Extension" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
            <column name="Extension" />
          </property>
          <property name="Public" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
            <column name="Public" />
          </property>
          <property name="ForAdults" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
            <column name="ForAdults" />
          </property>
          <subclass name="FolkeLib.MMOBash.Bash, FolkeLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" discriminator-value="5">
            <property name="Game" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
              <column name="Game" />
            </property>
            <property name="Language" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
              <column name="Language" />
            </property>
            <many-to-one class="FolkeLib.Domain.Account, FolkeLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="ApprovedBy">
              <column name="ApprovedBy_id" />
            </many-to-one>
          </subclass>
        </subclass>
      </subclass>
    </subclass>
  </class>
</hibernate-mapping>
1
what mapping/table do you see when commenting out BashMap? - Firo
If I comment out BashMap, then Fluent complains about ImageMap. If I comment out ImageMap, Fluent complains about Article. Etc.. - Astaar
can you enumerate all classes of Account's assembly and print out all classnames which are typeof(IMappingProvider).IsAssignableFrom(type) to see if the mappings are duplicated somehow? - Firo
I have edited the OP with the code you suggested, @Firo. - Astaar
you are right. Subclassmaps are of type IIndeterminateSubclassMappingProvider. Another unlikely cause could be that two subclasses inherit from SubclassMap<Bash> and since they have the same properties it doesnt give compilererrors - Firo

1 Answers

1
votes

Somebody found the reason. There was an error in the mappings of another class:

This was the issue:

public class ArticleListMap : SubclassMap<Article>
{
   ArticleListMap()
   {
      DiscriminatorValue(1);
   }
}

public class ArticleMap : SubclassMap<Article>
{
   ArticleMap()
   {
      DiscriminatorValue(4);
      Map(x => x.ExtraText);
      HasManyToMany(x => x.TagSet);
      HasManyToMany(x => x.ImageSet);
   }
}

Instead of inheriting ArticleListMap from SubclassMap<ArticleList> I was inheriting from SubclassMap<Article> (which was a typing mistake). Since they have different discriminators but inherit from the same class there was, indeed, a duplicate mappings than then "propagated" to the children classes.