【发布时间】: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 仍然必须执行查询并加载所有数据。 -
Instructor和Student是角色。一个人可以两者兼有。这应该使用单独的实体来模拟不同的角色,而不是继承。或者你应该使用两个完全不相关的实体——仅仅因为一些实体具有相似的属性并不意味着它们之间存在继承关系。 -
首先创建一个好的数据库模式。有单独的学生和教师表有意义吗?或者您是否需要
Persons,它可以是不同课程的学生或讲师?在这种情况下,您需要CourseInstructors和CourseStudents表。 -
@PanagiotisKanavos 我只是在这里写了一个简单的例子。我没有任何人、学生或教师。在这个简单的例子中,我只是用这个词来调用类。
-
即使在 C# 中,
Person也是 abstract 类,此代码删除了 concrete 类。在此之后没有实体可以重新加载。就 EF 而言,加载了与另一个现有人员具有相同 ID 的幽灵讲师
标签: c# entity-framework