2011-04-27 62 views
11

我在我的数据库3个表:NHibernate的 “级联=” 全删除,孤儿”错误

  1. 项目(ID,姓名)
  2. 标签(ID,姓名)
  3. ProjectsTagss(ID,专案编号,标签识别)

正如你可以看到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); 

但是当我去更新标签对象的名称和提交,我得到这个错误:

与级联集合=“全删除-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; } 
} 
+1

告诉我们在哪儿更新标签的代码。请写一个有意义的标题来表达这个问题。 – 2011-04-27 11:38:12

+0

@Stefan Steinegger - 更新标题以反映错误 – leora 2011-04-27 12:35:48

+1

当您从列表中删除项目并在另一个列表中重新使用项目时,会发生错误。所以请向我们展示您编写的用于更新数据的代码。 – 2011-04-28 08:20:49

回答

7

虽然集合不会被改动,NH仍然可以认为它是。像这样的东西可能是由鬼魂更新引起的。从NHibernate 3.0 Cookbook,Jason Dentler(第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表应该只包含标签和项目的ID编号,那么这将是简单的使用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(); 
16

某处在你的代码,你应该有取消您项目域上的原始集合。我怀疑你的代码是这样的:

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

是的,这是我们的问题。任何有关可能导致它的原因? – VoodooChild 2013-05-07 20:23:56

+0

导致它的原因是NHibernate“接管”了“ProjectTags”列表并跟踪其中的所有内容。通过替换拥有实体上的整个列表,你断开了这个跟踪列表,但它仍然被跟踪,并保存NHibernate不知道如何处理里面的所有对象。 – codekaizen 2013-07-08 23:41:58