Fluent NHibernate: How do I match many-to-many relationships with additional attributes in a relationship table?

I'm trying to map a many-to-many relationship between two objects, but I need to decorate this object with a lot of properties - see the diagram below:

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

Reading is my relationship table in this case. I added an identifier column to it to avoid using a composite key, but the valuable information here really is UserId, FeedItemId and TimeRead Attribute. Here's how I tried to match this relationship based on similar examples that I saw on StackOverFlow:

User

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

Reads

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

This code does not cause an error as is, but nothing is ever saved in the Reads table when I try to do the following:

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

This is probably due to the fact that both the user and FeedItem have .Inverse in the relationship mapping - I did it this way because this is what I saw in most other examples that tried to model the same relationship.

When I remove .Inverse from user mapping, I get this error instead:

NHibernate.TransientObjectException: the object refers to an unsaved instance transient - save the temporary instance before flushing.

My ultimate goal is to be able to do Session.SaveOrUpdate (user) and insert any new reading channels directly into the Reads table, but I have not yet been able to find a way to do this. What am I doing wrong?

I read almost any other StackOverFlow question about this topic and did not find a clear answer to this question.

+4
source share
1 answer

It all depends on how you want to keep new readings.

If you want to keep new readings through the user, you will need Cacascade.AllDeleteOrphan () for the user. Be that as it may, it will not be a cascade of new readings, since you only have 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"); } } 

Just a thought, I always like to minimize bi-directional relationships, trying to simplify the task. Therefore, I probably would not have collections on User or FeedItem if this could have been avoided.

+5
source

Source: https://habr.com/ru/post/1333134/


All Articles