NHibernate " cascade=" all-delete-orphan " ошибка

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

  1. проекты (id, name)
  2. Теги (id, name)
  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();

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

 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 ответов


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

обновление Ghost коллекции может быть вызвано кодом, который выглядит вот так:

public class Tag
{
    private IList<ProjectTag> projectsTags;

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

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

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

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

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 должна содержать только id тега и id проекта, тогда было бы проще использовать двунаправленное отображение FNH many-to-many:

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

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

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