Ошибка nhibernate "cascade =" all-delete-orphan " - PullRequest
11 голосов
/ 27 апреля 2011

У меня есть 3 таблицы в моей базе данных:

  1. Проекты (идентификатор, имя)
  2. Теги (идентификатор, имя)
  3. ProjectsTagss (id, projectId, tagid)

Как вы можете видеть, таблица ProjectsTags является таблицей мостов

вот мой беглыйОтображение nhibernate

ProjectMap.cs:

 Map(x => x.Name).Not.Nullable();
 HasMany(x => x.ProjectsTags).AsBag().Inverse()
    .Cascade.AllDeleteOrphan().Fetch.Select().BatchSize(80);

ProjectsTagsMap.cs:

 References(x => x.Project).Not.Nullable();
 References(x => x.Tag).Not.Nullable();

TagMap.cs: ​​

  Map(x => x.Name).Not.Nullable();

Как вы можете видеть, исторически у меня не было таблицы Tag, связанной с чем-либо еще.Теперь мне нужно сгенерировать отчет, чтобы показать тег и как часто этот тег используется, поэтому мне нужно присоединиться от тега к ProjectsTag.я попытался добавить эту строку в карту тегов:

 HasMany(x => x.ProjectsTags).AsBag().Inverse()
    .Cascade.AllDeleteOrphan().Fetch.Select().BatchSize(80);

, но когда я иду, чтобы обновить имя объекта тега и зафиксировать, я получаю эту ошибку:

Коллекция сcascade = "all-delete-orphan" больше не использовался экземпляром объекта-владельца

Может кто-нибудь увидеть что-то не так с тем, что я добавил, что будет вызывать это исключение nhibernate, когда я просто обновлю таблицу тегов,Опять же, моя цель состоит в том, чтобы иметь возможность сделать что-то вроде:

 Tag.ProjectTags.Count();

Вот некоторый дополнительный запрошенный код:

мой класс тегов:

 public class Tag
{
    public virtual IList<ProjectTag> ProjectTags { get; set; }
    public virtual string Name { get; set; }
    public virtual string Description { get; set; }
}

Ответы [ 2 ]

19 голосов
/ 04 мая 2011

Где-то в вашем коде вы должны были разыменовать исходную коллекцию в вашем домене Project. Я подозреваю, что ваш код выглядит так:

var project = Session.Get<Project>();
project.ProjectsTags = new List<ProjectsTags> { someProjectsTagsInstance };
Session.Save(project);

Если это так, вы должны сделать это вместо:

var project = Session.Get<Project>();
project.ProjectsTags.Clear();
project.ProjectsTags.Add(someProjectsTagsInstance);
Session.Save(project);
7 голосов
/ 05 мая 2011

Хотя коллекция не изменена, NH все еще может думать, что это так.Нечто подобное может быть вызвано призрачным обновлением.Из книги поваренных книг NHibernate 3.0, Джейсон Дентлер (стр. 184): «Как часть автоматической грязной проверки, NHibernate сравнивает исходное состояние объекта с его текущим состоянием. В противном случае неизмененный объект может быть излишне обновлен, потому что преобразование типа привело к сбою этого сравнения".

Призрачное обновление коллекции может быть вызвано кодом, который выглядит следующим образом:

public class Tag
{
    private IList<ProjectTag> projectsTags;

    public virtual IEnumerable<ProjectTag> ProjectsTags
    {
        get
        {
            return new ReadOnlyCollection<ProjectTag>(projectsTags);
        }

        set
        {
            projectsTags = (IList<ProjectTag>)value;
        }
    }
}

Свойство ProjectsTags возвращает коллекцию в оболочке только для чтения, поэтому клиентский код не может добавлять или удалять элементы в /из коллекции.

Ошибка будет появляться, даже если имя тега не изменено:

private void GhostTagUpdate(int id)
{
    using (var session = OpenSession())
    {
        using (var transaction = session.BeginTransaction())
        {
            var tag = session.Get<Tag>(id);

            transaction.Commit();
        }
    }
}

Коллекция ProjectsTags должна быть сопоставлена ​​со стратегией доступа CamelCaseField, чтобы избежать появления обновлений:

HasMany(x => x.ProjectsTags)
    .Access.CamelCaseField()
    .AsBag().Inverse().Cascade.AllDeleteOrphan().Fetch.Select().BatchSize(80);

В любом случае ...

Ваша связь кажется дьявольски сложной.Если таблица ProjectsTags должна содержать только идентификатор тега и идентификатор проекта, то было бы проще использовать двунаправленное отображение FNH «многие ко многим»:

public class Tag2Map : ClassMap<Tag2>
{
    public Tag2Map()
    {
        Id(x => x.Id);
        Map(x => x.Name);
        HasManyToMany(x => x.Projects)
            .AsBag()
            .Cascade.None()
            .Table("ProjectsTags")
            .ParentKeyColumn("TagId")
            .ChildKeyColumn("ProjectId");
    }
}

public class Project2Map : ClassMap<Project2>
{
    public Project2Map()
    {
        Id(x => x.Id);
        Map(x => x.Name);
        HasManyToMany(x => x.Tags)
            .AsBag()
            .Cascade.None()
            .Inverse()
            .Table("ProjectsTags")
            .ParentKeyColumn("ProjectId")
            .ChildKeyColumn("TagId");
    }
}

Теперь в модели не требуется сущность ProjectTag.,Счетчик количества использованных тегов может быть получен двумя способами:

Прямой путь: tag.Projects.Count() - но он извлекает все проекты из базы данных.

Способ запроса:

var tag = session.Get<Tag2>(tagId);
var count = session.Query<Project2>().Where(x => x.Tags.Contains(tag)).Count();
...