简体   繁体   English

实体框架属性中的虚拟关键字

[英]Virtual keyword in Entity Framework properties

public class Student
{
    public int StudentId;
    public string StudentName;
    public int CourseId;
    public virtual Course Courses { get; set; }
}

public class Course
{
    public int CourseId;
    public string CourseName;
    public string Description;
    public ICollection<Student> Students {get;set;}
    public ICollection<Lecture> Lectures { get; set; }
}

public class Lecture
{
    public int LectureId;
    public string LectureName;
    public int CourseId;
    public virtual Course Courses { get; set; }
}

What is the keyword virtual used for here?这里使用的关键字virtual是什么?

I was told a virtual is for lazy loading but I don't understand why.有人告诉我虚拟是为了延迟加载,但我不明白为什么。 Because when we do因为当我们做

_context.Lecture.FirstOrDefault()

the result returns the first Lecture and it does not include the attribute Course .结果返回第一个Lecture并且它不包括属性Course

To get the Lecture with the Course , we have to use:要获得带有CourseLecture ,我们必须使用:

_context.Lecture.Include("Courses").FirstOrDefault()

without using a virtual keyword, it's already a lazy-loading.不使用virtual关键字,它已经是一个延迟加载。

Then why do we need the keyword?那为什么我们需要关键字呢?

By declaring it virtual you allow EF to substitute the value property with a proxy to enable lazy loading.通过将其声明为virtual ,您允许 EF 将 value 属性替换为代理以启用延迟加载。 Using Include() is telling the EF query to eager -load the related data.使用Include()是告诉 EF 查询预先加载相关数据。

In EF6 and prior, lazy loading was enabled by default.在 EF6 及之前的版本中,默认启用延迟加载。 With EF Core it is disabled by default.对于 EF Core,默认情况下它是禁用的。 (Or not supported in the earliest versions) (或在最早的版本中不支持)

Take the following query:进行以下查询:

var lecture = _context.Lecture.Single(x => x.LectureId == lectureId);

to load one lecture.加载一堂课。

If you omit virtual then accessing lecture.Course would do one of two things.如果您省略virtual然后访问lecture.Course将做两件事之一。 If the DbContext (_context) was not already tracking an instance of the Course that lecture.CourseId was pointing at, lecture.Course would return #null.如果 DbContext (_context) 尚未跟踪lecture.CourseId 指向的课程实例,则lecture.Course 将返回#null。 If the DbContext was already tracking that instance, then lecture.Course would return that instance.如果 DbContext 已经在跟踪该实例,则 Lecture.Course 将返回该实例。 So without lazy loading you might, or might not get a reference, don't count on it being there.因此,如果没有延迟加载,您可能会或可能不会获得参考,不要指望它在那里。

With virtual and lazy loading in the same scenario, the proxy checks if the Course has been provided by the DbContext and returns it if so.在同一场景中使用virtual加载和延迟加载,代理会检查 Course 是否已由 DbContext 提供,如果是则返回。 If it hasn't been loaded then it will automatically go to the DbContext if it is still in scope and attempt to query it.如果它还没有被加载,那么它会自动 go 到 DbContext 如果它仍然在 scope 并尝试查询它。 In this way if you access lecture.Course you can count on it being returned if there is a record in the DB.这样,如果您访问lecture.Course ,如果数据库中有记录,您可以指望它被返回。

Think of lazy loading as a safety net.将延迟加载视为安全网。 It comes with a potentially significant performance cost if relied on, but one could argue that a performance hit is the lesser of two evils compared to runtime bugs with inconsistent data.如果依赖它会带来潜在的显着性能成本,但有人可能会争辩说,与具有不一致数据的运行时错误相比,性能损失是两害相权取其轻。 This can be very evident with collections of related entities.这在相关实体的 collections 中非常明显。 In your above example the ICollection<Student> and such should be marked as virtual as well to ensure those can lazy load.在您上面的示例中, ICollection<Student>等也应标记为virtual ,以确保它们可以延迟加载。 Without that you would get back whatever students might have been tracked at the time, which can be very inconsistent data state at runtime.否则,您将取回当时可能跟踪的任何学生,这可能是运行时非常不一致的数据 state。

Take for example you have 2 courses, Course #1 and #2.例如,您有 2 门课程,课程 #1 和 #2。 There are 4 students, A, B, C, and D. All 4 are registered to Course #1 and only A & B are registered to Course B. If we ignore lazy-loading by removing the virtual then the behavior will change depending on which course we load first if we happen to eager-load in one case and forget in the second...有 4 名学生,A、B、C 和 D。所有 4 人都注册到课程 #1,只有 A 和 B 注册到课程 B。如果我们通过删除virtual来忽略延迟加载,那么行为将根据如果我们碰巧在一种情况下急切加载而在第二种情况下忘记了,我们首先加载哪个课程......

using (var context = new MyAppDbContext())
{
    var course1 = context.Courses
        .Include(x => x.Students)
        .Single(x => x.CourseId == 1);
    var course2 = context.Courses
        .Single(x => x.CourseId == 2);

    var studentCount = course2.Students.Count();
}

Disclaimer: With collections in entities you should ensure these are always initialized so they are ready to go.免责声明:在实体中使用 collections 时,您应该确保它们始终被初始化,以便它们准备好 go。 This can be done in the constructor or on an auto-property:这可以在构造函数或自动属性中完成:

public ICollection<Student> Students { get; set; } = new List<Student>();

In the above example, studentCount would come back as "2" because in loading Course #1, both Student A & B were loaded via the Include(x => x.Students) This is a pretty obvious example loading the two courses right after one another but this situation can easily occur when loading multiple records that share data, such as search results, etc. It is also affected by how long the DbContext has been alive.在上面的示例中,studentCount 将返回为“2”,因为在加载课程 #1 时,学生 A 和 B 都是通过Include(x => x.Students)加载的但这种情况在加载共享数据的多条记录时很容易发生,例如搜索结果等。它还受到 DbContext 存活时间的影响。 This example uses a using block for a new DbContext instance scope, one scoped to the web request or such could be tracking related instances from earlier in the call.此示例对新的 DbContext 实例 scope using块,该实例的范围为 web 请求,或者可以跟踪调用早期的相关实例。

Now reverse the scenario:现在反转场景:

using (var context = new MyAppDbContext())
{
    var course2 = context.Courses
        .Include(x => x.Students)
        .Single(x => x.CourseId == 2);
    var course1 = context.Courses
        .Single(x => x.CourseId == 1);

    var studentCount = course1.Students.Count();
}

In this case, only Students A & B were eager loaded.在这种情况下,只有学生 A 和 B 被急切加载。 While Course 1 actually references 4 students, studentCount here would return "2" for the two students associated with Course 1 that the DbContext was tracking when Course 1 was loaded.虽然课程 1 实际上引用了 4 个学生,但这里的 studentCount 将为与课程 1 关联的两个学生返回“2”,当课程 1 加载时 DbContext 正在跟踪这些学生。 You might expect 4, or 0 knowing that you didn't eager-load the students.知道您没有预先加载学生,您可能会期望 4 或 0。 The resulting related data is unreliable and what you might or might not get back will be situational.生成的相关数据是不可靠的,您可能会或可能不会返回的内容将取决于情景。

Where lazy loading will get expensive is when loading sets of data.延迟加载会变得昂贵的地方是加载数据集。 Say we load a list of 100 students and when working with those students we access student.Course.假设我们加载了一个包含 100 名学生的列表,并且在与这些学生一起工作时,我们访问了 student.Course。 Eager loading will generate 1 SQL statement to load 100 students and their related courses.急切加载将生成 1 个 SQL 语句以加载 100 名学生及其相关课程。 Lazy loading will end up executing 1 query for the students, then 100 queries to load course for each student.延迟加载最终会为学生执行 1 个查询,然后为每个学生执行 100 个查询来加载课程。 (Ie SELECT * FROM Courses WHERE StudentId = 1; SELECT * FROM Courses WHERE StudentId = 2; ...) If student had several lazy loaded properties then that's another 100 queries per lazy load. (即 SELECT * FROM Courses WHERE StudentId = 1; SELECT * FROM Courses WHERE StudentId = 2; ...)如果学生有几个延迟加载的属性,那么每个延迟加载还有 100 个查询。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 virtual关键字对Entity Framework 5中的实体属性有什么影响? - What effect does the virtual keyword have on entity properties in Entity Framework 5? 实体框架:为什么使用虚拟关键字在DbContext中声明属性 - Entity Framework: Why virtual keyword used to declare properties in DbContext 实体框架虚拟属性 - Entity Framework Virtual Properties Entity Framework Core 6:属性虚拟与非虚拟 - Entity Framework Core 6 : properties virtual vs non virtual 实体框架虚拟属性和Id属性的数据库访问? - Entity Framework virtual properties and database access for Id properties? 实体框架-通过虚拟关键字的延迟加载-检测循环引用 - Entity Framework - lazy load through virtual keyword - detect circular references 为什么在实体框架模型定义中对类属性使用“虚拟”? - Why use 'virtual' for class properties in Entity Framework model definitions? 实体框架 - Include()加载所有chidren属性,甚至是虚拟属性 - Entity framework - Include() loads all chidren properties even virtual ones 实体框架4.1:创建新实体后如何访问虚拟实体属性? - Entity Framework 4.1: How can I access virtual entity properties after creating a new entity? 实体框架 - 虚拟财产 - Entity Framework - Virtual Property
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM