NHibernate " cascade=" all-delete-orphan " ошибка
у меня есть 3 таблицы в моей базе:
- проекты (id, name)
- Теги (id, name)
- 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);