【问题标题】:"not-null property references a null or transient value" when deleting entity using NHibernate使用 NHibernate 删除实体时,“非空属性引用空值或瞬态值”
【发布时间】:2017-11-24 14:01:38
【问题描述】:

我正在编写一个简单的应用程序来学习新技术。我一直在为我的应用程序编写单元测试:

    [Test]
    public void LessonChangeSubjectShouldNotAffectFormerSubjectPersistence()
    {
        // given
        Lesson ExampleLesson = TestDataFactory.CreateLesson();
        Save(ExampleLesson);
        Refresh(ref ExampleLesson);
        Subject formerSubject = ExampleLesson.Subject;

        // when
        Subject subject = TestDataFactory.CreateSubject();
        _subjectService.Save(subject);
        ExampleLesson.ChangeSubject(subject);
        Update(ExampleLesson);
        _subjectService.Update(formerSubject);
        Refresh(ref ExampleLesson);
        formerSubject = _subjectService.Get(formerSubject.Id);
        subject = _subjectService.Get(subject.Id);

        // then
        ExampleLesson.Subject.Should().Be(subject);
        formerSubject.Should().NotBeNull();
        formerSubject.Lessons.Should().NotContain(ExampleLesson);

        Refresh(ref ExampleLesson);

        _subjectService.Delete(formerSubject);
        Refresh(ref ExampleLesson);
        Delete(ExampleLesson);
    }

这个场景包括:

  1. 使用默认随机主题创建课程并保存
  2. 创建新主题并将课程主题更改为新主题
  3. 检查旧主题是否仍然存在

这个场景成功了,但是当涉及到清理测试时,最后一行出现错误

NHibernate.PropertyValueException : 非空属性引用空值或瞬态值 Register.Core.Model.Lesson.Subject

Lesson类中的Subject字段映射如下:

References(x => x.Subject)
.Cascade.SaveUpdate()
.Column("SubjectId")
.Fetch.Select()
.ForeignKey("FK_Lessons_Subjects")
.Index("IX_Subject")
.LazyLoad()
.Not.Nullable();

Subject 类中的 Lessons 集合映射为:

HasMany(x => x.Lessons)
.Access.CamelCaseField(Prefix.Underscore)
.Cascade.All()
.Fetch.Select()
.ForeignKeyConstraintName("FK_Lessons_Subjects")
.Inverse()
.KeyColumn("SubjectId")
.Not.KeyNullable()
.LazyLoad();

当我交换时

    _subjectService.Delete(formerSubject);

    Refresh(ref ExampleLesson);
    Delete(ExampleLesson);

没有来自 NHibernate 的投诉和测试通过。我很好奇发生了什么以及为什么顺序很重要。如果有人能指出我的问题的解决方案,我也将不胜感激。提前谢谢你。

【问题讨论】:

  • @RadimKöhler 现在删除 formerSubject 也会删除 ExampleLesson,这是不正确的行为,因为我们将主题更改为另一个
  • @RadimKöhler 它之前得到了一个新的父母,因为测试将课程更改为新的课程并保存它。课程在旧课程被删除之前获得新父母。

标签: c# nhibernate fluent-nhibernate nunit


【解决方案1】:

错误可能是由于别名问题。该课程有两份副本,其中一份可以通过前一个主题访问,可能是因为课程集合不受刷新影响。

我的建模建议:

主题不应包含课程集

您必须一直不必要地刷新它而不使用它来保持模型有效。它会随着时间的推移变得巨大并带来各种问题。您将倾向于对其进行过滤/枚举/排序,这将是一个内存操作而不是数据库调用。

do not abstract away the ORM

为实际业务逻辑提供服务。 DataAccessObjects (DAO) 是多余的,因为 NHibernate 的 ISession 已经是一个。用于单元测试的 Sqlite 内存数据库的优势在于还可以测试查询以返回正确的内容。

示例代码

class Lesson : Entity
{
    public virtual string Name { get; set; }
    public virtual Subject Subject { get; set; }

    public void ChangeSubject(Subject subject)
    {
        // do some checks

        Subject = subject;
    }
}

class Subject : Entity
{
    public string Name { get; set; }
}


[Test]
public void LessonChangeSubjectShouldNotAffectFormerSubjectPersistence()
{
    var session = GetInMemorySession();
    // given
    Lesson exampleLesson = new Lesson { Name = "Lesson1", Subject = new Subject { Name = "Subject1" } };
    session.Save(exampleLesson.Subject);    // because we havent set cascade.save
    session.Save(exampleLesson);
    var formerSubjectId = exampleLesson.Subject.Id;

    // when
    Subject subject = new Subject { Name = "Subject2" };
    session.Save(subject);
    exampleLesson.ChangeSubject(subject);
    session.Flush();    // save all changes
    session.Clear();

    var formerSubject = session.Get<Subject>(formerSubjectId);
    subject = session.Get<Subject>(subject.Id);

    // then
    exampleLesson.Subject.Should().Be(subject);
    formerSubject.Should().NotBeNull();

    // technically not needed because the inmemory database is gone after this test
    session.Delete(formerSubject);
    session.Delete(subject);
}

【讨论】:

    猜你喜欢
    • 2010-10-08
    • 2013-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-25
    • 1970-01-01
    相关资源
    最近更新 更多