我正在学习如何使用具有NHibernate背景的Entity Framework。 网上有很多教程,但我没有找到适合我具体案例的内容。 我不想删除类别记录,我只想删除关系!
我关注了poco's:
public class TrainingCourse
{
[Key]
public int CourseId { get; set; }
public string CourseName { get; set; }
public virtual ICollection<TrainingContent> Content { get; set; } = new List<TrainingContent>();
}
public class TrainingContent
{
[Key]
public int ContentId { get; set; }
public string ContentName { get; set; }
public int? CategoryId { get; set; }
[ForeignKey("CategoryId")]
public Category Category { get; set; }
}
public class Category
{
public int CategoryId { get; set; }
public string CategoryName { get; set; }
}
在数据库中,我有以下数据&#39;插入
var category = new Category { CategoryName = "Category 1" };
var course = new TrainingCourse { CourseName = "Course 1" };
context.TrainingCourses.Add(course);
var content = new TrainingContent { ContentName = "Content 1", Category = category };
context.TrainingContents.Add(content);
course.Content.Add(content);
我现在想删除从TrainingContent到Category的关系:在数据库术语中,我想在表TrainingContent中将我的外键CategoryId设置为null。
在一个上下文中执行此操作时,它正常工作,保存后我的外键为NULL:
using (var context = new ClientContext())
{
_course = context.TrainingCourses.Include(c => c.Content.Select(cat => cat.Category)).FirstOrDefault(n => n.CourseName == "Course 1");
_course.Content.ToList()[0].ContentName = "Content 1 changed";
_course.Content.ToList()[0].Category = null;
context.SaveChanges();
}
但在现实世界中,我们正在处理断开连接的实体。 我用以下代码模拟这个
using (var context = new ClientContext())
{
_course = context.TrainingCourses.Include(c => c.Content.Select(cat => cat.Category)).FirstOrDefault(n => n.CourseName == "Course 1");
}
_course.Content.ToList()[0].ContentName = "Content 1 changed";
_course.Content.ToList()[0].Category = null;
using (var context = new ClientContext())
{
context.Entry(_course.Content.ToList()[0]).State = EntityState.Modified;
context.SaveChanges();
}
这不起作用,内容名称已更改,但我的外键仍然已定义。
如果我将navigation属性设置为NULL并将FK设置为NULL,则它正在运行:
using (var context = new ClientContext())
{
_course = context.TrainingCourses.Include(c => c.Content.Select(cat => cat.Category)).FirstOrDefault(n => n.CourseName == "Course 1");
}
_course.Content.ToList()[0].ContentName = "Course 1 changed";
_course.Content.ToList()[0].Category = null;
_course.Content.ToList()[0].CategoryId = null;
using (var context = new ClientContext())
{
context.Entry(_course.Content.ToList()[0]).State = EntityState.Modified;
context.SaveChanges();
}
为什么会这样?我是EF和EF Core的新手,所以也许有一个简单的解释? 我最初使用Entity Framework Core对此进行了测试,但行为相同。
答案 0 :(得分:0)
您的问题是,如果导航属性为null,则EF不会检查参照完整性(这将在SQL端发生)。
然而,这在大多数情况下是必要的。您可能希望将内容附加到其他类别:
var content=context.Contents.FirstOrDefault();
content.CategoryId=2;
context.Entry(content).State=EntityState.Modified;
context.SaveChanges();
看起来很好,不是吗?但是,请注意,您从未实际加载(延迟或显式)相应的Category对象 - 它仍为null。
因此,在此示例中以及在您的示例中,代码本身看起来很好,但FK和导航属性不匹配。但是,由于导航属性没有是正确的(除非被上下文跟踪,因此它被加载并且EF知道它是正确的),EF将期望FK是正确的,因此保存这个值进入数据库。
请注意,在少数情况下不会发生此问题:第一个显然是EF能够在其ChangeTracker中跟踪类别 - 然后它会知道您实际上想要考虑类别导航属性,而不一定是FK。 第二个是当你的FK不属于你的对象时。从那时起,EF必须创建一个FK,但是你不能自己设置它。然后EF必须设置与导航属性对应的FK。 (请注意,此不会使用null。null表示未加载值或值为什么(以及其他一些含义) )并且EF无法确定哪一个是正确的。
另外,请记住,在断开连接的场景中,EF会在附加该树的根时检查整个对象树的参照完整性 - 如果任何FK与导航属性不匹配,EF将抛出异常(由于上述原因,null除外)。 并且:当您在断开连接的场景中工作时,请注意当任何父条目处于已添加状态时,必须附加所有子对象(atm我不知道它们是否也必须被修改/新建),否则EF无法修复儿童对象的FK并抛出异常。