Search code examples
c#nhibernatenhibernate-mapping-by-code

NHibernate mapping-by-code equivalent of <composite-index> for composite Dictionary keys


Is it possible to map a dictionary with a multi-column key using mapping-by-code? I have not yet found an equivalent to <composite-index> yet.

Example entities:

public class Warehouse
{
    // ctors

    private IDictionary<StorageCoordinates, StoragePosition> _storagePositions;

    public virtual string Id { get;  protected set; }

    public virtual IEnumerable<StoragePosition> StoragePositions
    {
        get { return _storagePositions.Values; }
    }

    public virtual StoragePosition GetStoragePosition(StorageCoordinates coordinates)
    {
        return _storagePositions[coordinates];
    }
}

public class StoragePosition
{
    // ctors

    public virtual StorageCoordinates Coordinates { get; protected set; }

    public virtual bool IsOccupied { get; set; }
}

public struct StorageCoordinates
{
    public int X { get; set; }
    public int Y { get; set; }
    public int Z { get; set; }
}

With XML, I can map Warehouse._storagePositions like this:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <class name="Warehouse, Domain" table="Warehouse">
    <id name="Id" type="string" length="50" column="Id" />
    <map name="StoragePositions" access="field.camelcase-underscore" cascade="all-delete-orphan">
      <key column="Warehouse" />
      <composite-index class="StorageCoordinates, Domain">
        <key-property name="X" />
        <key-property name="Y" />
        <key-property name="Z" />
      </composite-index>
      <one-to-many class="StoragePosition, Domain" />
    </map>
  </class>
</hibernate-mapping>

I'm not sure if and how I can translate this into a (conformist) code mapping. This is how far I have gotten:

class WarehouseMap : ClassMapping<Warehouse>
{
    public WarehouseMap()
    {
        Table("Warehouse");
        Id(x => x.Id, x => x.Column("Id"));
        Map<StorageCoordinates, StoragePosition>("_storagePositions",
            x =>
            {
                x.Cascade(Cascade.All | Cascade.DeleteOrphans);
                x.Key(k => k.Column(c => c.Name("Warehouse")));
            },
            x =>
            {
                x.Element(k => k.Columns
                (
                    c => c.Name("X"),
                    c => c.Name("Y"),
                    c => c.Name("Z"))
                );
            },
            x => x.OneToMany(m => m.Class(typeof(StoragePosition))));
    }
}

This will however yield a MappingException:

"Could not determine type for: StorageCoordinates, Domain, for columns: NHibernate.Mapping.Column(X), NHibernate.Mapping.Column(Y), NHibernate.Mapping.Column(Z)"

Any insights on this would be much appreciated! Unfortunately, the documentation on mapping-by-code seems to be non-existent :-(. I tried to use Fluent NHibernate, but that seems to be even worse with Dictionary mapping.


Solution

  • Seems like I got it the mapping right after all - the only problem was that StorageCoordinates was a struct. After making it a class, the mapping works. Probably wouldn't have worked with XML mapping either.

    UPDATE: Apparently, explicitly mapping the composite Dictionary key is not even necessary at all with mapping-by-code. NHibernate seems to figure it out by itself somehow.

    As much as I love NHibernate, a little more documentation here and there would be nice ...