我使用Class1
到Class2
之间的多对多关系。完全像这样:
public class Class1{
[Key]
public int Id { get; set; }
public virtual IList<Class2> Classes2 { get; set; }
//...
}
public class Class2{
[Key]
public int Id { get; set; }
//... no navigational parameter
// there is no need for navigational when using fluent API below.
// However the navigational presence does not affects the issue
}
和流畅的API
OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Entity<Class1>().HasMany(a => a.Classes2).WithMany();
}
EF使用Class1Class2
中间表创建所有表模式。
然后我的应用程序将Class1
打印到视图中,并将修改后的Class1
从用户可以删除的用户绑定回来,或添加Classes2
。 (我已经检查过,所有数据都在entity
实例中正确绑定)。
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind()] Class1 entity) {
if (ModelState.IsValid) {
db.Entry(entity).State = EntityState.Modified;
db.SaveChanges();
}
return View();
}
但是SaveChanges
不会更新多对多的数据。
如何正确更新新绑定的多个参数?
我只想删除或添加关系,我不想添加或删除Classes2记录。
答案 0 :(得分:2)
由于对被问及的内容存在误解,我已经取代了我在这里给出的原始答案。
随着后来的清理,我意识到旧答案与这个问题无关。
所以,这是我对EF进行多对多映射,并更新所涉及的关系。
我已经使用过&#39; EF&#39;在实体(TeacherEFs
,StudentEFs
和TeacherEFsStudentEFs
)的名称中,实体框架将使用它们进行多对多的映射。
其他人(Teachers
,Students
,TeachersStudents
)用于控制所有表格数据的普通CRUD操作。
虽然您可以使用流畅的API,但数据注释足以设置它,下面显示了两种方法的示例实体:
// Manual method - you control the relationship table
public class Teacher
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual IList<TeachersStudents> TeachersStudents { get; set; }
}
public class Student
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual IList<TeachersStudents> TeachersStudents { get; set; }
}
public class TeachersStudents
{
[Key]
public int Id { get; set; }
[Index("IX_Teacher_Student", 1)]
public int TeacherId { get; set; }
[Index("IX_Teacher_Student", 2)]
public int StudentId { get; set; }
[ForeignKey("TeacherId")]
public virtual Teacher Teacher { get; set; }
[ForeignKey("StudentId")]
public virtual Student Student { get; set; }
}
// Automatic method - Entity Framework controls the relationship table
public class TeacherEF
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual IList<StudentEF> StudentEFs { get; set; }
}
public class StudentEF
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual IList<TeacherEF> TeacherEFs { get; set; }
}
这通常是为上述实体生成迁移的结果:
public override void Up()
{
CreateTable(
"dbo.TeacherEFs",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(),
})
.PrimaryKey(t => t.Id);
CreateTable(
"dbo.StudentEFs",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(),
})
.PrimaryKey(t => t.Id);
CreateTable(
"dbo.Teachers",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(),
})
.PrimaryKey(t => t.Id);
CreateTable(
"dbo.Students",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(),
})
.PrimaryKey(t => t.Id);
CreateTable(
"dbo.TeacherEFStudentEFs",
c => new
{
TeacherEF_Id = c.Int(nullable: false),
StudentEF_Id = c.Int(nullable: false),
})
.PrimaryKey(t => new { t.TeacherEF_Id, t.StudentEF_Id })
.ForeignKey("dbo.TeacherEFs", t => t.TeacherEF_Id, cascadeDelete: true)
.ForeignKey("dbo.StudentEFs", t => t.StudentEF_Id, cascadeDelete: true)
.Index(t => t.TeacherEF_Id)
.Index(t => t.StudentEF_Id);
CreateTable(
"dbo.TeachersStudents",
c => new
{
Id = c.Int(nullable: false, identity: true),
TeacherId = c.Int(nullable: false),
StudentId = c.Int(nullable: false),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.Teachers", t => t.TeacherId, cascadeDelete: true)
.ForeignKey("dbo.Students", t => t.StudentId, cascadeDelete: true)
.Index(t => new { t.TeacherId, t.StudentId }, name: "IX_Teacher_Student");
}
请注意,Entity Framework使用我们在类中提供的关系向数据库添加了第三个表:
public virtual ICollection<TeacherEF> TeachersEF { get; set; }
public virtual ICollection<StudentEF> StudentsEF { get; set; }
它使用了两个实体的Id
属性,并将它们组合在一起以创建复合和唯一主键。
现在可以看出,如果您在TeacherEFs
和StudentEFs
之间添加关系,这些关系将放在此表中 - 将不会有任何关于父表中关系的信息({{ 1}}和TeacherEFs
)。
还有一个Cascade Delete集,这样如果从StudentEFs
表中删除了TeacherEF
,则TeacherEFs
表中与TeacherEF
的所有关系都将被删除(防止孤立的数据)。
因此,要添加或删除TeacherEFsStudentEFs
和TeacherEFs
之间的关系,此第三个表格是唯一要更新的表格。
此关系数据可由Entity Framework自动更新,也可由您自己手动控制。
就个人而言,我更喜欢控制它(因为我喜欢直接知道数据发生了什么);正如您从上面所看到的,为了控制这一点,您可能希望自己使用StudentEFs
表,随意添加和删除TeachersStudents
和Teachers
的ID(使用任何现有的Id)。
另一种方法是让实体框架在幕后为您完成工作,但在这种情况下,您需要让实体框架知道StudentEFs
表中通过{TeacherEFsStudentEFs
表的每个更改。 1}}和TeacherEF
个实体。
由于您无法直接访问关系表,因此您需要加载所有会更改关系的StudentEF
和TeacherEFs
,即添加或删除。然后将每个实体的状态设置为是要添加还是删除相关项。然后,实体框架将确定需要更改关系表(StudentEFs
)中的哪些行,然后最终执行操作。
就个人而言,我发现这有点令人费解,你必须在执行任何必要的更新之前点击数据库。这就是我更喜欢控制关系表中的数据并且仅在TeacherEFsStudentEFs
我们将介绍两种情况:
您选择一名教师,然后与学生联系。
您要做的是将这两个ID放入关系表中。
方法1 (我的偏好)。
正常执行的手动CRUD:
可以使用SaveChanges();
实体完成此操作,使用所选TeachersStudents
和Id
的{{1}}填充这两个属性,然后调用Teacher
(普通CRUD具有关系表的实体)。
但是,如果其他人添加了同样的关系怎么办?它会在唯一约束上失败!您可以优雅地处理此错误,或者如果它有意义,您可以通知用户他们尝试添加的特定关系已经存在。
您需要选择Student
条记录(包含SaveChanges()
,TeachersStudents
和Id
),并加载TeacherId
和StudentId
数据用于显示(最好使用视图模型),以及可供编辑选择的备选列表。
现在,您可以让用户更改Teacher
并使用Student
方法更新其Student
和新Id
相关的现有记录。
此方法允许您将单个实体(或多个,如果允许在UI上一次删除多个)发送到服务器,使用Delete方法删除关系,
以上优点是使用基本CRUD以相同的方式处理数据库表中的所有数据。
我不会为方法1的关系表的CRUD操作提供代码示例,因为我们都知道如何做到这一点。
方法2。
使用Entity Framework完成工作:
除了最初加载UI上显示的所有数据(如方法1所述),当您将更改发送回服务器时,您需要:
循环传入已收到的StudentId
,并从存储中检索他们的数据及其相关的Edit
。
循环传入TeacherEFs
并将每一个与从存储中检索到的内容进行比较,看看是否添加或删除了StudentEFs
。
将每个存储的TeacherEFs
StudentEF
个收藏集设置为适当添加和/或删除。
最后,您可以致电TeacherEF
。
实体框架将在实体&#39;之后为您执行StudentEF
表格所需的编辑。关系已经确定。
这类似于您为实体创建CRUD页面(例如,对于TeacherCourse详细信息),但从不为其加载Id和现有属性。
现在,用户可以将各种数据添加到属性中(如果他们知道,则指定ID),但在发送回服务器时,您不知道自己拥有什么 - 您将不得不询问存储以查看课程是否存在,然后检查它是否是添加,编辑或删除,然后执行所需的操作。
以下是如何执行实体框架&#39;黑匣子&#39;办法。
该示例使用单个SaveChanges()
并更新其TeacherEFsStudentEFs
集合。
如果您希望将多个TeacherEF
发送到服务器进行更新,只需更改代码以循环访问该集合。
StudentEF