Search code examples
c#nhibernatenhibernate-mapping

how to map to a SortedList in NHibernate


I have an nHibernate parent child table relationship. The parent class is currently pulling the children into a List but I want to put them in a SortedList based on an ordering column in the table. How do I change the NHibernate mapping file to let the system know which column I am ordering on?

The current NHibernate mapping files are:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   namespace="Model.Configuration.Pages"
                   assembly="Model.Configuration">
  <joined-subclass
            table="PageConfigurations"
            name="PageConfiguration"
            extends="Model.Configuration.Configuration.TargetedConfiguration"
            lazy="false">
    <key column="TargetedConfigurationId" />

    <property name="SchemaVersion" />

    <property name="Template" type="System.String" length="50" />

    <property name="PageKey" type="System.String" length="50" />

    <property name="Percentage" />

    <bag name="Controls" cascade="all-delete-orphan" lazy="false" >
      <key column="PageConfigurationId" />
      <one-to-many class="WidgetConfiguration"/>
    </bag>

  </joined-subclass>
</hibernate-mapping>

for the parent table and:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   namespace="Model.Configuration.Pages"
                   assembly="Model.Configuration">
  <class name="WidgetConfiguration" lazy="false" table="PageConfiguration_Widgets" discriminator-value="Default">
    <id name="Id"  unsaved-value="0" type="int" >
      <generator class="identity" />
    </id>
    <discriminator column="ConfigurationType" />

    <property name="Slot" type="System.String" length="100" />
    <property name="WidgetTypeName" type="System.String" length="100"/>
    <property name="ViewName" type="System.String" length="50" />
    <property name="SlotOrder" type="System.Int32" />
  </class>
</hibernate-mapping>

for the child table.

What do I need to add to the parent or child mappings to let them know that the new SlotOrder column should be used as the key field when fetching the WidgetConfiguration into a SortedList.

EDIT: The class that the data is being read into is:

public class PageConfiguration : TargetedConfiguration 
{

    public PageConfiguration()
    {
        // replaced by SortedList
        //Controls = new List<WidgetConfiguration>();
        Controls = new SortedList<int, WidgetConfiguration>();
    }

    public string PageKey { get; set; }
    public string Template { get; set; }
    public int? Percentage { get; set; }
    // replaced by SortedList
    //public IList<WidgetConfiguration> Controls { get; set; }
    public IDictionary<int, WidgetConfiguration> Controls { get; set; }
    public int SchemaVersion { get; set; }
}

Notice that the List<WidgetConfiguration> has been changed to SortedList<int, WidgetConfiguration>. How do I tell NHibernate that when a new item is added to the SortedList that the key value used should be WidgetConfiguration.SlotOrder?


Solution

  • NHiberante has nice support for what you would like to get. In fact there is not only one way, there are two:

    1) mapping with the <list>

    Use the list mapping which supports the <index> column. this is the same as the internal index in the C# used inside the List<T> object

    Small disadvantage is, that you have to keep that column to be from 0 ... including all numbers. The same as the underlying C# object. but it works

    Please see more here: http://ayende.com/blog/4043/nhibernate-mapping-list and here for indexed collections: http://nhibernate.info/doc/nh/en/index.html#collections-ofvalues

    2) extend the mapping with the order-by="column_name asc|desc"

    This allows to use some more user-friendly column (with some editable value) to be used for the sorting of the list during its loading

    Plase, see more here: 6.2. Mapping a Collection

    EDIT: to follow the Question Edit

    C# SortedList mapping is again supported by NHibernate. Please, se the section 6.6. Sorted Collections

    Mapping should look like this

    <map name="Controls" order-by="SlotOrder" lazy="true" cascade="all-delete-orphan">
        <key column="PageConfigurationId" />
        <index column="SlotOrder" type="System.Int32"/>
        <one-to-many class="WidgetConfiguration"/>
    </map>
    

    the SlotOrder is now managed by the Parent. It plays the role of the key of the Controls SortedList. So we should change its mapping to be readonly (only one editing point in time should exist)

    <class name="WidgetConfiguration" ...>
      ...
      <property name="SlotOrder" type="System.Int32" insert="false" update="false" />
    

    Having this mapping, we can add new item into Controls collection:

    WidgetConfiguration config = ...;
    PageConfiguration pageConfig = ...;
    
    pageConfig.Controls[config.SlotOrder] = config;
    session.Update(pageConfig);
    

    So,

    1. we have mapped the SortedList (in fact interface IDictionary<,>),
    2. the SlotOrder is used as a key (inserted, updated by NHibernate) and
    3. available (as readonly) on the WidgetConfiguration

    That should answer the question about sorting and keys of dictionaries... please, let me know if I did miss something...