Fluent NHibernate:如何在关系表上映射具有附加属性的多对多关系?

Posted

技术标签:

【中文标题】Fluent NHibernate:如何在关系表上映射具有附加属性的多对多关系?【英文标题】:Fluent NHibernate: How do I map Many-to-Many Relationships with additional attributes on the relationship table? 【发布时间】:2010-12-23 05:32:53 【问题描述】:

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

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

用户

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");
    


此代码不会按原样引发错误,但当我尝试执行以下操作时,读取表中不会保留任何内容:

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(user) 并将任何新的提要读取直接插入到读取表中,但我还没有找到一种方法来做到这一点。我做错了什么?

我已经阅读了 *** 上有关此主题的几乎所有其他问题,但尚未找到该问题的明确答案。

【问题讨论】:

【参考方案1】:

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

如果你想通过用户保存新的读取,那么你需要用户的 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 的集合。

【讨论】:

呃,我太傻了——我以为我把它设置为 Cascade.AllDeleteOrphan——谢谢你指出我的错误!

以上是关于Fluent NHibernate:如何在关系表上映射具有附加属性的多对多关系?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Fluent NHibernate 中将一对一关系映射为复合键的一部分

NHibernate + Fluent NHibernate 异常

使用 Fluent NHibernate 映射或模仿多对任意关系的选项?

Fluent NHibernate:约定/KeyColumn

如何使用 Fluent 设置 NHibernate.Burrow?

如何在 Fluent NHibernate ClassMap 类中指定表名?