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

发布于 2024-11-03 08:01:20 字数 1455 浏览 0 评论 0原文

我的数据库中有3个表:

  1. Projects (id, name)
  2. Tags (id, name)
  3. ProjectsTagss (id, projectId, tagid)

As你可以看到 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。我尝试将这一行添加到标签映射中:

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

i have 3 tables in my database:

  1. Projects (id, name)
  2. Tags (id, name)
  3. ProjectsTagss (id, projectId, tagid)

As you can see the ProjectsTags table is a bridge table

here is my fluent nhibernate mapping

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

As you can see, i historically didn't have the Tag table linked to anything else. 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. 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:

A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance

can anyone see anything wrong with what i added that would be causing this nhibernate exception when i simply update the Tag table. Again my goal is to be able to do something like:

 Tag.ProjectTags.Count();

Here is some additional code as requested:

my Tag Class:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

新雨望断虹 2024-11-10 08:01:20

在代码中的某个位置,您应该取消引用项目域上的原始集合。我怀疑你的代码是这样的:

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

Somewhere in your code, you should have dereferenced the original collection on your Project domain. 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);
北风几吹夏 2024-11-10 08:01:20

虽然集合没有被修改,但 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 和项目的 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();

While a collection is not modified, NH can still think that it is. 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".

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.

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:

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:

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. 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.

Query way:

var tag = session.Get<Tag2>(tagId);
var count = session.Query<Project2>().Where(x => x.Tags.Contains(tag)).Count();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文