views:

276

answers:

2

I have a base class for content items in a CMS I'm building. It's currently marked abstract because I only want derived classes to be instantiated. Derived classes like BlogPost, Article, Photo, etc. are set up as a joined subclass to my ContentBase class in nHibernate.

I'm trying to set up a many-to-many mapping between this class and a Tag class. I want to have a collection of Tags on the ContentBase class, and a collection of ContentBase items on the tag class.

Will nHibernate allow me to map the abstract ContentBase class as a collection on the Tag class? I'm assuming not since it wouldn't be able to instantiate any instances of this class when reconstituting a Tag entity from the db. I really don't want to have to have to use a collection of content items per type (e.g. TaggedBlogPosts, TaggedArticles, etc.) on the Tag class.

The whole reason I'm doing this is because logically, a content item can have many tags, and 1 tag can belong to multiple content items. in order for nHibernate to manage the relationships for me in a mapping table, I believe I have to set up a many-to-many association and add the Tag to the ContentBase.Tags collection and then the content item to the Tags.TaggedContentItems collection before the mapping table entry is created in nHibernate.

Here are my mappings for reference:

  <class name="CMS.Core.Model.Tag,CMS.Core" table="bp_Tags">
    <id column="TagName" name="TagName" type="String" unsaved-value="">
      <generator class="assigned" />
    </id>
    <bag name="_taggedContentList" table="bp_Tags_Mappings" inverse="true" cascade="save-update" lazy="true">
      <key column="TagName" />
      <many-to-many class="CMS.Core.Model.ContentBase,CMS.Core" column="Target_Id" />
    </bag>
  </class>

  <class name="CMS.Core.Model.ContentBase,CMS.Core" table="bp_Content">
    <id name="Id" column="Id" type="Int32" unsaved-value="0">
      <generator class="native"></generator>
    </id>
    <property name="SubmittedBy" column="SubmittedBy" type="string" length="256" not-null="true" />
    <property name="SubmittedDate" column="SubmittedDate" type="datetime" not-null="true" />
    <property name="PublishDate" column="PublishDate" type="datetime" not-null="true" />
    <property name="State" column="State" type="CMS.Core.Model.ContentStates,CMS.Core" not-null="true" />
    <property name="ContentType" column="ContentType" type="CMS.Core.Model.ContentTypes,CMS.Core" not-null="true" />

    <bag name="_tagsList" table="bp_Tags_Mappings" lazy="false" cascade="save-update">
      <key column="Target_Id" />
      <many-to-many class="CMS.Core.Model.Tag,CMS.Core" column="TagName" lazy="false" />
    </bag>
    ...
        <joined-subclass name="CMS.Core.Model.BlogPost,CMS.Core" table="bp_Content_BlogPosts" >
          <key column="Id" />
          <property name="Body" type="string" column="Body" />
          <property name="Title" type="string" column="Title" />
        </joined-subclass>
    ...
A: 

I would also assume that Hibernate would need to instantiate the base class, which makes sense, as there is data directly linked to it in the DB.

It looks like your data entities are in a separate assembly from your main application. The point of not instantiating the base is from a business point of view, so if you make the constructor internal to the Core assembly, does that accomplish what you want? If not, it may be helpful to ask yourself: who am I protecting this functionality from?

Jon Seigel
A: 

NHibernate should allow you to map the abstract class as a collection as long as you map the abstract class itself (it looks like you have).

An alternative is to change your strategy to the Table-per-class-hierachy approach. This puts all your content into a single table with a discriminator column to define the type of content. NHibernate knows how to materialize each content type based on the discriminator. The downside is that:

  1. Each unique property of the concrete classes has to be of type nullable.
  2. This table can get unwieldy if there are a lot of properties specific to each concrete class.

The table-per-class-hierachy is the preferred default mapping strategy (per NHibernate in Action). I would start with the approach and modify it to table-per-subclass when the need is certain.

A whole separate solution would be keep the mapping as is and rely on query to get the content by tag without worrying about the tags keeping a reference to a collection of content.

Michael Gattuso