客户端生成的ID,PK是父级的复合键。 NHibernate不高兴。备择方案?

时间:2013-04-02 20:19:14

标签: c# nhibernate nhibernate-mapping composite-key

我正在开发一个客户端应用程序,它将数据传递回数据库。客户端能够创建PlaylistItem类型的对象。在创建PlaylistItem时,我不等待数据库使用生成的ID进行响应。相反,我让客户端生成ID,但是使用PK {PlaylistID,PlaylistItemID}将PlaylistItem写入数据库。 PlaylistID由服务器生成。 I followed this approach after talking things over a bit with Jon Skeet

现在,我正试图让NHibernate的东西变得生气勃勃,但我遇到了一些相当沉重的问题。我读过的所有资源都在说,“NHibernate严重反对使用复合键。只有在你使用传统数据库时才使用它们。”我不是在处理遗留数据库,所以我认为我应该做出改变。但是,在这种情况下,我不知道我的替代方案会是什么。

这是PlaylistItem的NHibernate映射和相应的类:

<?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>

[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);
    }
}

NHibernate抛出异常,并显示错误消息“此SqlParameterCollection的索引号无效,且Count = n”。我明白在hbm.xml文件中有重复声明时会出现这种情况。根据我的理解,这会产生,因为我将PlaylistId定义为关键属性,并再次定义为多对一关系。

我有什么选择?我很难过。

1 个答案:

答案 0 :(得分:1)

您可以使用key-many-to-one代替key-property,即

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

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

</class>

然后你的班级看起来像......

[DataContract]
public class PlaylistItem
{
  // Your composite key...
  [DataMember(Name = "id")]
  public Guid Id { get; set; }    
  public Playlist Playlist { 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; }

  // rest of class...
}