Свободный NHibernate: Как мне сопоставить отношения «многие ко многим» с дополнительными атрибутами в таблице отношений? - PullRequest
4 голосов
/ 23 декабря 2010

Я пытаюсь отобразить отношение многие ко многим между двумя сущностями, но мне нужно украсить эту сущность рядом свойств - см. Диаграмму ниже:

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

Reads - это моя таблица отношений в этом случае - я добавил в нее столбец идентификаторов, чтобы избежать использования составного ключа, но ценная информация здесь - это на самом деле 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");
    }
}

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

Этот код не вызывает ошибок как есть, но в таблице 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: объект ссылается на несохраненный временный экземпляр - сохраните временный экземпляр перед сбросом.

Моя конечная цель - иметь возможность выполнять Session.SaveOrUpdate (user) и иметь возможность вставлять любой новый канал для чтения напрямую.в таблицу чтения,но я пока не смог найти способ сделать это.Что я делаю не так?

Я уже почти все остальные вопросы на StackOverFlow прочитал по этой теме и не нашел четкого ответа на этот вопрос.

1 Ответ

5 голосов
/ 23 декабря 2010

Все зависит от того, как вы хотите сохранить новые чтения.

Если вы хотите сохранить новые Чтения через Пользователя, вам необходим 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, если бы этого можно было избежать.

...