I'm trying to map a many-to-many relationship between two entities, but I need to decorate that entity with a number of properties - see the diagram below:
Reads is my relationship table in this case - I added an identity column on it in order to avoid using a composite key, but the valuable information here is really the UserId, the FeedItemId, and the TimeRead attribute. Here is how I have tried to map this relationship, based on similar examples I've seen 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 doesn't raise errors as-is, but nothing is ever persisted to 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 because of the fact that both User and FeedItem have a .Inverse on the relationship mapping - I did it this way because it's what I saw in most other examples that tried to model this same relationship.
When I remove the .Inverse off of the User mapping, I get this error instead:
NHibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing.
My ultimate goal is to be able to do Session.SaveOrUpdate(user) and have it insert any new feed reads directly into the Reads table, but I haven't been able to find a way to do it yet. What am I doing wrong?
I've read through virtually every other question on StackOverFlow about this topic already and haven't found a clear answer to this question.