I'm hoping not to argue semantics and "you should not use a composite key" 
with this post. I understand the implications of composite key use, but 
believe it to be best suited for my task at hand.

I have the following in "PlaylistItem.hbm.xml:"

<?xml version="1.0" encoding="utf-8" ?><hibernate-mapping 
xmlns="urn:nhibernate-mapping-2.2" assembly="Streamus" 
namespace="Streamus.Backend.Domain">

  <class name="PlaylistItem" table="[PlaylistItems]" lazy="false" >
    <composite-id>
      <key-property name="Id" />
      <key-property name="PlaylistId"/>
    </composite-id>

    <property name="Title" not-null="true" />

    <many-to-one name="Playlist" column="PlaylistId"/>
  </class>
</hibernate-mapping>

which corresponds with the following class:

[DataContract]public class PlaylistItem{
    [DataMember(Name = "playlistId")]
    public Guid PlaylistId
    {
        get { return Playlist.Id; }
        set { Playlist.Id = value; }
    }

    public Playlist Playlist { get; set; }

    [DataMember(Name = "id")]
    public Guid Id { get; set; }

    //  Store Title on PlaylistItem as well as on Video because user might want 
to rename PlaylistItem.
    [DataMember(Name = "title")]
    public string Title { get; set; }

    public PlaylistItem()
    {
        //  Id shall be generated by the client. This is OK because it is 
composite key with 
        //  PlaylistId which is generated by the server. 
        Id = Guid.Empty;
        Title = string.Empty;
    }

    private int? _oldHashCode;
    public override int GetHashCode()
    {
        // Once we have a hash code we'll never change it
        if (_oldHashCode.HasValue)
            return _oldHashCode.Value;

        bool thisIsTransient = Equals(Id, Guid.Empty);

        // When this instance is transient, we use the base GetHashCode()
        // and remember it, so an instance can NEVER change its hash code.
        if (thisIsTransient)
        {
            _oldHashCode = base.GetHashCode();
            return _oldHashCode.Value;
        }
        return Id.GetHashCode();
    }

    public override bool Equals(object obj)
    {
        PlaylistItem other = obj as PlaylistItem;
        if (other == null)
            return false;

        // handle the case of comparing two NEW objects
        bool otherIsTransient = Equals(other.Id, Guid.Empty);
        bool thisIsTransient = Equals(Id, Guid.Empty);
        if (otherIsTransient && thisIsTransient)
            return ReferenceEquals(other, this);

        return other.Id.Equals(Id);
    }}


This clearly generates an exception with NHibernate because the property 
"PlaylistId" is referenced through the composite key definition as well as 
in the many-to-one definition. I am obstinate about using PlaylistId as 
part of the composite key. I would also like to leverage as much of 
NHibernate's built-in capabilities in order to preserve data integrity.

I've implemented the following as a work-around, but I am not happy with my 
change. I am not using NHibernate's ability to automatically dehydrate 
Playlist when loading a PlaylistItem:

public Guid PlaylistId{
    get
    {
        return Playlist == null ? Guid.Empty : Playlist.Id;
    }
    set
    {
        if (Playlist == null || Playlist.Id != value)
        {
            Playlist = new PlaylistDao().Get(value);
        }
    }}

in addition to this change, I remove <many-to-one name="Playlist" column=
"PlaylistId"/>

This works as expected. Whenever NHibernate dehydrates the PlaylistId via 
the composite key, PlaylistItem's Playlist property is set. However, the 
code obviously smells. I am wondering -- do I have any options here? Or, is 
this an unsupported scenario in NHibernate?

Thanks

-- 
You received this message because you are subscribed to the Google Groups 
"nhusers" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/nhusers?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.


Reply via email to