NHibernate custom collection won't hydrate

306 Views Asked by At

I have a custom collection that wraps a .net HashSet and implements ICollection, which I then map as a set. I did this in the hope that NHib would be able to use the ICollection interface to the way it can do when using just a .net HashSet, as opposed to the Iesi one that NHib uses internally.

The save to the db seems to be working fine, but the exception I get when hydrating lets me know I need to do more:

Unable to cast object of type 'NHibernate.Collection.Generic.PersistentGenericSet`1[ContactMechanisms.Domain.Emails.Email]' to type 'ContactMechanisms.Domain.Emails.EmailCollection'.

These articles are frequently cited as ways to deal with custom collection handling, but the links are broken and what I can see deals more with querying the collection with extensions.

Must I use an IUserCollectionType? Anyone have a link showing a sample implementation if so? Is there something silly I am doing in my current code / mapping?

What's a good solution?

CODE (parent entity snippet)

public class Contact : RoleEntity<Party>, IHaveContactMechanisms
{       
    private readonly ICollection<Email> _emails = new EmailCollection();

    public virtual EmailCollection Emails { get { return (EmailCollection) _emails; } }
}

CODE (custom collection snippet)

 public class EmailCollection : ContactMechanismSet<Email> { ....  }

 public class ContactMechanismSet<T> : ValueObject, ICollection<T> where T : ContactMechanism
{
    private readonly HashSet<T> _set = new HashSet<T>();
}

MAPPING (hbm value type collection)

<set name ="_emails" table="Emails" access="field">
  <key column="ContactId"></key>
  <composite-element class="ContactMechanisms.Domain.Emails.Email, ContactMechanisms.Domain">
    <property name="IsPrimary" />
    <property name="Address" length="100"/>
    <property name="DisplayName" length="50"/>
  </composite-element>
</set>

* UPDATE *

SO doing the below in my object setter works, but - can I do better??

public virtual EmailCollection Emails {
    get {
        if (!(_emails is EmailCollection )) {
            // NHibernate is giving us an enumerable collection
            // of emails but doesn't know how to turn that into
            // the custom collection we really want
            //
            _emails = new EmailCollection (_emails );
        }
        return (EmailCollection ) _emails ;
    }
}
1

There are 1 best solutions below

0
On

if EmailCollection has a parameterless constructor then the following should be possible with newer NH ootb and older with registering CollectionTypeFactory which handles .NET ISet (can be found in internet)

public class Contact : RoleEntity<Party>, IHaveContactMechanisms
{       
    private EmailCollection _emails = new EmailCollection();

    public virtual EmailCollection Emails { get { return _emails; } }
}

public class ContactMechanismSet<T> : ValueObject, ICollection<T> where T : ContactMechanism
{
    ctor()
    {
        InternalSet = new HashSet<T>();
    }

    private ISet<T> InternalSet { get; set; }
}

<component name="_emails">
  <set name="InternalSet" table="Emails">
    <key column="ContactId"></key>
    <composite-element class="ContactMechanisms.Domain.Emails.Email, ContactMechanisms.Domain">
      <property name="IsPrimary" />
      <property name="Address" length="100"/>
      <property name="DisplayName" length="50"/>
    </composite-element>
  </set>
</component>