【问题标题】:How to reload entity in EntityFramework?如何在 EntityFramework 中重新加载实体?
【发布时间】:2020-01-22 07:01:53
【问题描述】:

我有一个课程:

public class abstract Person{
    public int Id{set;get;}
    public string Name {set;get;}
}

public class Student:Person{
    public string Specialisation {set;get;}
}

public class Instructor:Person{
   public decimal Salary {set;get;}
}

在数据库中,我有这些表:人员、学生、教师。

现在在我的应用程序中,我想将其中一名学生更改为讲师。我的意思是我需要更改实体类型但我需要保存相同的Id。我没有找到如何使用 EF 执行此操作,所以我只使用 SQL 脚本:

using (var command = db.Database.Connection.CreateCommand())
            {
                if (command.Connection.State == ConnectionState.Closed) command.Connection.Open();

                if (db.Database.CurrentTransaction != null)
                    command.Transaction = db.Database.CurrentTransaction.UnderlyingTransaction;

                command.CommandText = $@"delete from [dbo].[Students] where Id = @id;
                                        insert into [dbo].[Instructors](Id) values(@id)";

                command.Parameters.Add(new SqlParameter("@id", SqlDbType.Int) { Value = myId });
                command.ExecuteNonQuery();
            }

但是当我尝试在相同的上下文中获得这个新讲师时,我得到了错误:

   var instructor = db.Instructors.FirstOrDefault(t=>t.Id = myId);

   All objects in the EntitySet 'myContext.Person' must have unique primary keys. However, an instance of type 'Person.Instructor' and an instance of type 'Person.Student' both have the same primary key value, 'EntitySet=Person;Id=1'. 

我尝试重新加载基础实体:

var myPerson = db.Persons.FirstOrDefault(t=>t.Id = myId);

///
my sql where i delete Student and insert Instructor
///

db.Entry(myPerson).Reload();

var instructor = db.Instructors.FirstOrDefault(t=>t.Id = myId);

但是得到了同样的错误。那么如何正确地做到这一点呢?
我的意思是也许我的解决方案完全错误?或者如果不是这样,我该如何重新加载实体?

【问题讨论】:

  • 错了好吧。您不是在更改实体,而是在一个表中删除 行并在另一个表中创建可能不相关的行。 EF 对此一无所知,因此当您调用 FirstOrDefault 时,它最终会加载一个与不同实体具有相同 ID 的实体。您也无法通过此代码获得任何收益 - EF 仍然必须执行查询并加载所有数据。
  • InstructorStudent角色。一个人可以两者兼有。这应该使用单独的实体来模拟不同的角色,而不是继承。或者你应该使用两个完全不相关的实体——仅仅因为一些实体具有相似的属性并不意味着它们之间存在继承关系。
  • 首先创建一个好的数据库模式。有单独的学生和教师表有意义吗?或者您是否需要Persons,它可以是不同课程的学生或讲师?在这种情况下,您需要 CourseInstructorsCourseStudents 表。
  • @PanagiotisKanavos 我只是在这里写了一个简单的例子。我没有任何人、学生或教师。在这个简单的例子中,我只是用这个词来调用类。
  • 即使在 C# 中,Person 也是 abstract 类,此代码删除了 concrete 类。在此之后没有实体可以重新加载。就 EF 而言,加载了与另一个现有人员具有相同 ID 的幽灵讲师

标签: c# entity-framework


【解决方案1】:

Sean Bean 的 Boromir 的模因声明“一个人不会简单地改变实体的类型”是合适的。简单来说,Dog 和 Cat 可以从 Animal 继承,但 Dog 永远不会变成 Cat。

一个人是一个实体。他们所做的是一个角色,而不是一个实体,或者如果它是一个实体,它可以与那个人相关联。

例如,使用 Person、Instructor、Student、Course:

课程有一名教师和一名学生。如果学生和教师扩展 Person,则学生的 PK 是 PersonId,而教师的 PK 是 PersonID。我的表结构可能类似于:

Person [PersonId, Name, ... 常用字段]

讲师 [PersonId,...讲师字段]

学生 [PersonId, ... 学生字段]

课程 [CourseId, InstructorPersonId, Name, ... 课程字段]

CourseStudent [CourseId, StudentPersonId]

或者,Person/Student/Instructor 模式可以是:

人员 [PersonId, Role, Name, ...]

其中 Role 是 EF 可以用来区分 Instructor 和 Student 继承的鉴别器。无论哪种方式,它本质上都不是一个好的模型,因为什么可以确保课程的讲师实际上指向与 Instructor 行关联的 Person?如果您只是删除某人的讲师行以使他们成为学生,那么课程仍然可以将他们视为讲师。

相反,您应该考虑关联。人不会变,只是角色变了。每门课程都需要一名讲师,并且会有学生,并且可能会有特定于该讲师(资格)和学生的详细信息。 (成绩)

人员 [PersonId, Name, ...]

Role [RoleId, Name] // 即“导师”

PersonRole [PersonId, RoleId]

Instructor [InstructorId, PersonId, ...] // 认为是“讲师职位”

Student [StudentId, PersonId ...] // 可以认为是“学生点”

课程 [CourseId, InstructorId, Name, ...]

StudentCourse [StudentId, CourseId]

在这种情况下,Instructor 和 Student 不从 Person 继承,它们与一个人相关联。一个人可以持有与角色的关联,当一个人可以与讲师职位关联或不关联时,可以考虑这些关联。该应用程序仍需要规范有关关联的规则,例如如果讲师职位由另一个人担任,并确保课程没有相同的“人”担任讲师和学生,并且学生位置没有换人。 (成绩不转移)

通过这种方式,您可以独立于背后的人员创建/管理教师和学生。 “学生”作为一个实体是完全可选的,相反,CourseStudent 可以只关联 PersonId,而诸如成绩之类的东西可以直接关联到 Person。例如,一个学生并且有成绩的人获得了讲师的角色并且可以与课程的讲师职位相关联,这一事实并没有破坏任何东西。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-01-04
    • 2020-03-01
    • 2016-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-05
    • 1970-01-01
    相关资源
    最近更新 更多