流畅的NHibernate:如何在关系表上使用其他属性映射多对多关系?

时间:2010-12-23 05:32:53

标签: c# nhibernate orm fluent-nhibernate nhibernate-mapping

我正在尝试映射两个实体之间的多对多关系,但我需要使用许多属性来装饰该实体 - 请参阅下图:

Many-to-Many Relationship with additional attributes on relationship table

在这种情况下,

读取是我的关系表 - 我在其上添加了一个标识列以避免使用复合键,但这里有价值的信息实际上是UserId,FeedItemId和TimeRead属性。以下是我尝试根据我在StackOverFlow上看到的类似示例尝试映射此关系的方法:

用户

public class UserMap : ClassMap<User>
{
    public UserMap()
    {
        Id(x => x.UserId).GeneratedBy.Identity();
        Map(x => x.UserName).Length(DataConstants.UserNameLength).Unique().Not.Nullable();
        Map(x => x.EmailAddress).Length(DataConstants.EmailAddressLength).Unique().Not.Nullable();
        Map(x => x.DateJoined).Not.Nullable();
        Map(x => x.Password).Length(DataConstants.PasswordHashLength).Not.Nullable();
        HasManyToMany(x => x.UserRoles).Cascade.AllDeleteOrphan().AsBag().Table("UsersInRole");
        HasManyToMany(x => x.SubscribedFeeds).Cascade.DeleteOrphan().AsBag().Table("Subscriptions");
        HasManyToMany(x => x.OwnedFeeds).Cascade.All().AsBag().Table("FeedOwners");
        HasMany(x => x.Reads).Cascade.DeleteOrphan().Fetch.Join().Inverse().KeyColumn("UserId");
    }
}

FeedItem

public class FeedItemMap : ClassMap<FeedItem>
{
    public FeedItemMap()
    {
        Id(x => x.FeedItemId).GeneratedBy.Identity();
        Map(x => x.OriginalUri).Not.Nullable().Unique().Length(DataConstants.FeedUriLength);
        Map(x => x.DatePublished).Not.Nullable();
        Map(x => x.Title).Not.Nullable().Length(DataConstants.FeedItemTitleLength);
        References(x => x.Feed);
        HasManyToMany(x => x.Tags).Cascade.All().Table("PostTags");
        HasManyToMany(x => x.Categories).Cascade.All().Table("PostsInCategory");
        HasMany(x => x.Reads).Cascade.AllDeleteOrphan().Inverse().Fetch.Join().KeyColumn("FeedItemId");
    }
}

读取

public class FeedReadMap : ClassMap<FeedRead>
{
    public FeedReadMap()
    {
        Table("Reads");
        //CompositeId()
        //    .KeyProperty(x => x.TimeRead, "TimeRead")
        //    .KeyReference(x => x.ItemRead, "FeedItemId")
        //    .KeyReference(x => x.Reader, "UserId");
        Id(x => x.ReadId).GeneratedBy.Identity();
        Map(x => x.TimeRead).Not.Nullable();
        References(x => x.Reader).Not.Nullable().Cascade.SaveUpdate().Column("UserId");
        References(x => x.ItemRead).Not.Nullable().Cascade.SaveUpdate().Column("FeedItemId");
    }
}

此代码不会按原样引发错误,但在尝试执行以下操作时,Reads表中始终没有任何内容:

var read = new FeedRead {ItemRead = feed.Items[0], Reader = user, TimeRead = DateTime.Now};
        user.Reads.Add(read);
        feed.Items[0].Reads.Add(read);

        _repository.SaveUser(user);

这可能是因为User和FeedItem在关系映射上都有 .Inverse 这一事实 - 我是这样做的,因为它是我在大多数其他试图模拟这个的例子中看到的同样的关系。

当我从用户映射中删除.Inverse时,我得到了这个错误:

  

NHibernate.TransientObjectException:   对象引用未保存的瞬态   instance - 保存瞬态实例   在冲洗之前。

我的最终目标是能够执行Session.SaveOrUpdate(用户)并将其直接插入Reads表中,但我还没有找到办法。我做错了什么?

我已经在StackOverFlow上阅读了关于这个主题的几乎所有其他问题,并且没有找到这个问题的明确答案。

1 个答案:

答案 0 :(得分:5)

这完全取决于您希望如何保存新的阅读。

如果您想通过用户保存新的读取,那么您需要用户的Cacascade.AllDeleteOrphan()。就目前而言,它不会级联新的读取,因为你只有DeleteOrphan。

public class UserMap : ClassMap<User>
{
    public UserMap()
    {
        Id(x => x.UserId).GeneratedBy.Identity();
        Map(x => x.UserName).Length(DataConstants.UserNameLength).Unique().Not.Nullable();
        Map(x => x.EmailAddress).Length(DataConstants.EmailAddressLength).Unique().Not.Nullable();
        Map(x => x.DateJoined).Not.Nullable();
        Map(x => x.Password).Length(DataConstants.PasswordHashLength).Not.Nullable();
        HasManyToMany(x => x.UserRoles).Cascade.AllDeleteOrphan().AsBag().Table("UsersInRole");
        HasManyToMany(x => x.SubscribedFeeds).Cascade.DeleteOrphan().AsBag().Table("Subscriptions");
        HasManyToMany(x => x.OwnedFeeds).Cascade.All().AsBag().Table("FeedOwners");
        HasMany(x => x.Reads).Cascade.AllDeleteOrphan().Fetch.Join().Inverse().KeyColumn("UserId");
    }
}

只是一个想法,我总是希望尽量减少双向关系,以保持域更简单。因此,如果可以避免,我可能没有User或FeedItem上的集合。