简体   繁体   English

nhibernate “cascade=”all-delete-orphan”错误

[英]nhibernate “cascade=”all-delete-orphan" error

i have 3 tables in my database:我的数据库中有 3 个表:

  1. Projects (id, name)项目(ID,名称)
  2. Tags (id, name)标签(ID,名称)
  3. ProjectsTagss (id, projectId, tagid) ProjectsTagss (id, projectId, tagid)

As you can see the ProjectsTags table is a bridge table如您所见,ProjectsTags 表是一个桥接表

here is my fluent nhibernate mapping这是我流利的 nhibernate 映射

ProjectMap.cs:项目地图.cs:

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

ProjectsTagsMap.cs:项目TagsMap.cs:

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

TagMap.cs:标签映射.cs:

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

As you can see, i historically didn't have the Tag table linked to anything else.如您所见,我在历史上没有将 Tag 表链接到其他任何内容。 I now need to generate a report to show Tag and how often that tag is used so i need to join from Tag to ProjectsTag.我现在需要生成一个报告来显示标签以及该标签的使用频率,因此我需要从标签加入到 ProjectsTag。 i tried adding this line into the tagsmap:我尝试将此行添加到标签映射中:

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

but when i go to update the name on a tag object and commit, i get this error:但是当我 go 更新标签 object 上的名称并提交时,我收到此错误:

A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance拥有的实体实例不再引用具有 cascade="all-delete-orphan" 的集合

can anyone see anything wrong with what i added that would be causing this nhibernate exception when i simply update the Tag table.当我简单地更新标签表时,任何人都可以看到我添加的内容有什么问题会导致这个 nhibernate 异常。 Again my goal is to be able to do something like:同样,我的目标是能够执行以下操作:

 Tag.ProjectTags.Count();

Here is some additional code as requested:以下是根据要求的一些附加代码:

my Tag Class:我的标签 Class:

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

Somewhere in your code, you should have dereferenced the original collection on your Project domain.在您的代码中的某处,您应该取消引用 Project 域上的原始集合。 I suspect that your code goes like this:我怀疑你的代码是这样的:

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

If this is the case, you should do this instead:如果是这种情况,您应该这样做:

var project = Session.Get<Project>();
project.ProjectsTags.Clear();
project.ProjectsTags.Add(someProjectsTagsInstance);
Session.Save(project);

While a collection is not modified, NH can still think that it is.虽然集合没有被修改,NH 仍然可以认为它是。 Something like this could be caused by a ghost update.这样的事情可能是由幽灵更新引起的。 From NHibernate 3.0 Cookbook, Jason Dentler (page 184): "As part of automatic dirty checking, NHibernate compares the original state of an entity to its current state. An otherwise unchanged entity may be updated unnecessarily because a type conversion caused this comparison to fail". From NHibernate 3.0 Cookbook, Jason Dentler (page 184): "As part of automatic dirty checking, NHibernate compares the original state of an entity to its current state. An otherwise unchanged entity may be updated unnecessarily because a type conversion caused this comparison to fail ”。

Ghost update of collection can be caused by code that looks like this:集合的幽灵更新可能是由如下代码引起的:

public class Tag
{
    private IList<ProjectTag> projectsTags;

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

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

ProjectsTags property returns the collection in readonly wrapper, so client code cannot add or remove elements to/from the collection. ProjectsTags 属性返回只读包装器中的集合,因此客户端代码无法在集合中添加或删除元素。

The error will appear even when name of a tag is not changed:即使标签名称未更改,也会出现错误:

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

            transaction.Commit();
        }
    }
}

ProjectsTags collection should be mapped with CamelCaseField access strategy to avoid ghost updated: ProjectsTags 集合应使用 CamelCaseField 访问策略进行映射,以避免幽灵更新:

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

Anyway...反正...

Your association seems to be diabolically complex.你的联想似乎非常复杂。 If ProjectsTags table should contains only id of tag and id of project, then it would be simpler to use FNH many-to-many bidirectional mapping:如果 ProjectsTags 表应该只包含标签的 id 和项目的 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");
    }
}

Now there is no need for ProjectTag entity in the model.现在 model 中不需要 ProjectTag 实体。 The count of how many times is given tag used can be retrieved in two ways:可以通过两种方式检索给定标签使用次数的计数:

Direct way: tag.Projects.Count() - but it retrieves all projects from database.直接方式: tag.Projects.Count() - 但它从数据库中检索所有项目。

Query way:查询方式:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM