我有一个对象 PlaylistItem,它在通过 ID 引用其下一个 PlaylistItem 时保存得很好。尝试修改此代码以引用实际的 PlaylistItem 对象本身时遇到异常。我了解该问题是由我的 hbm.xml 文件中的重复映射引起的。但是,我无法为我的场景找到解决方案。
这是目前的 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-many-to-one name="Playlist" column="PlaylistId"/>
</composite-id>
<property name="Title" not-null="true" />
<many-to-one name="Video" column="VideoId" not-null="true" />
<many-to-one name="NextItem" not-null="true">
<column name="NextItemId" />
<column name="PlaylistId" />
</many-to-one>
</class>
</hibernate-mapping>
我可以通过修改 NextItem 多对一关系使其具有属性“insert="false" update="false" 来避免此异常。但是,这样做会导致 NextItem 在数据库中始终为 NULL。
这是相关的类:
public class PlaylistItem
{
public Guid Id { get; set; }
public Playlist Playlist { get; set; }
public PlaylistItem NextItem { get; set; }
public string Title { get; set; }
public Video Video { 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);
}
}
无论 NextItem 是否已定义或为 null,都会引发异常。我坚信我需要使用复合键来唯一地表示 PlaylistItem。
我有哪些选择?我不确定如何进一步解决这个问题。
这是保存前我的对象手表的屏幕截图:
如前所述,我还尝试在 NextItem 为空的情况下进行保存。此外,我尝试让 NextItem 完全成为另一个 PlaylistItem(这样 PlaylistItem 就不是自引用的。)这些更改都没有任何效果。
这是异常的屏幕截图:
异常显示为“Count=5 的此 SqlParamaterCollection 的索引 5 无效。” 没有定义内部异常。
以下是屏幕截图中引用的 PlaylistItem 的构造函数:
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;
}
public PlaylistItem(Guid id, string title, Video video)
: this()
{
Id = id;
Title = title;
Video = video;
}
这是 Playlist 的构造函数及其 AddItem 方法:
public Playlist()
{
Id = Guid.Empty;
Title = string.Empty;
Items = new List<PlaylistItem>();
}
public Playlist(string title)
: this()
{
Title = title;
}
/// <summary>
/// Add a PlaylistItem to the Playlist's PlaylistItem collection by inserting
/// the PlaylistItem at the end of the linked list. Adjust the links
/// to accomodate the PlaylistItem and set the Playlist's FirstItem
/// if necessary.
/// </summary>
public void AddItem(PlaylistItem playlistItem)
{
if (Items.Count == 0)
{
playlistItem.NextItem = playlistItem;
playlistItem.PreviousItem = playlistItem;
FirstItem = playlistItem;
}
else
{
// Adjust our linked list and add the item.
FirstItem.PreviousItem.NextItem = playlistItem;
playlistItem.PreviousItem = FirstItem.PreviousItem;
FirstItem.PreviousItem = playlistItem;
playlistItem.NextItem = FirstItem;
}
Items.Add(playlistItem);
playlistItem.Playlist = this;
}
这是 PlaylistManager 的构造函数及其 Save 方法:
public PlaylistManager(IPlaylistDao playlistDao, IPlaylistItemDao playlistItemDao, IVideoDao videoDao)
{
PlaylistDao = playlistDao;
PlaylistItemDao = playlistItemDao;
VideoDao = videoDao;
}
public void Save(Playlist playlist)
{
try
{
NHibernateSessionManager.Instance.BeginTransaction();
playlist.ValidateAndThrow();
PlaylistDao.Save(playlist);
NHibernateSessionManager.Instance.CommitTransaction();
}
catch (Exception exception)
{
Logger.Error(exception);
NHibernateSessionManager.Instance.RollbackTransaction();
throw;
}
}
PlaylistDao 的 Save 方法只是 NHibernateSession.Save(entity); 的一个包装器。