Traducción: Primeros pasos con Entity Framework 6 de MVC5 (7) -Lectura de datos relacionados para aplicaciones ASP.NET MVC

Leer datos relacionados para aplicaciones ASP.NET MVC

Esta es una traducción del tutorial oficial de Microsoft Comenzando con Entity Framework 6 Code Primero usando la serie MVC 5, aquí está el séptimo artículo: Lectura de datos relacionados para aplicaciones ASP.NET MVC

: :Lectura de datos relacionados con Entity Framework en una aplicación ASP.NET MVC


En el tutorial anterior, ha completado el modelo de datos de la escuela. En este tutorial aprenderá a leer datos relacionados con la realidad; esto se refiere a los datos cargados a través de las propiedades de navegación de Entity Framework.

La siguiente captura de pantalla muestra la página que está por completar.

cursos

ics

Retraso, precarga y datos relacionados con la carga realista

El marco de la entidad tiene una variedad de métodos para cargar datos relacionados de las propiedades de navegación de una entidad:

  • Carga perezosa
    . Cuando la entidad se lee por primera vez, no se obtendrán los datos relevantes. Sin embargo, cuando intenta acceder por primera vez a la propiedad de navegación, los datos requeridos por la propiedad de navegación se cargan automáticamente. El resultado se enviará a la base de datos utilizando múltiples consultas: una vez para leer la entidad en sí, luego cada entidad relacionada. La clase DbContext utiliza la carga diferida de forma predeterminada.

Windows-Live-Writer_Reading-Re.NET-MVC-Application-5-of-10h1_ADC3_Lazy_loading_example_2c44eabb-5fd3-485a-837d-8e3d053f2c0c

  • Ansioso de
    cargar. Cuando una entidad lee, inmediatamente obtiene datos relacionados con la entidad. Esto generalmente resulta en la recuperación de todos los datos requeridos en una sola consulta de conexión. Puede especificar la precarga utilizando el método Incluir.

Windows-Live-Writer_Reading-Re.NET-MVC-Application-5-of-10h1_ADC3_Eager_loading_example_33f907ff-f0b0-4057-8e75-05a8cacac807

  • Explícitamente carga (Explicit
    Loading). Es un poco similar a la carga diferida, excepto que obtiene explícitamente datos relevantes en su código. Cuando accede a una propiedad de navegación, no se cargará automáticamente. Debe cargar manualmente los datos relevantes mediante el uso del administrador de estado de objetos de la entidad y llamando al método Collection.Load en la colección o mediante el método Reference.Load que contiene las propiedades de una sola entidad. (En el siguiente ejemplo, si desea cargar las propiedades de navegación del administrador, debe usar Referencia (x
    => x.Administrator) para reemplazar la Colección (x => x.Courses))

Windows-Live-Writer_Reading-Re.NET-MVC-Application-5-of-10h1_ADC3_Explicit_loading_example_79d8c368-6d82-426f-be9a-2b443644ab15

Como ni la carga diferida ni la carga explícita recuperan el valor de un atributo de inmediato, también se denominan carga diferida.

Consideraciones de rendimiento

Si sabe que necesita los datos relevantes para cada entidad de inmediato, la precarga generalmente proporciona el mejor rendimiento. Porque la eficiencia de enviar una sola consulta a la base de datos y obtener los datos a la vez generalmente es mayor que la eficiencia de emitir otra consulta en cada entidad. Por ejemplo, en el ejemplo anterior, suponiendo que cada departamento tenga diez cursos relacionados, la carga previa dará como resultado una sola consulta (consulta conjunta) desde y hacia la base de datos. Tanto la carga diferida como la carga explícita generarán 11 consultas y viajes de ida y vuelta. En el caso de alta latencia, las consultas adicionales y los viajes de ida y vuelta suelen ser desventajosos.

Por otro lado, el uso de carga diferida es más eficiente en algunos casos. La precarga puede causar consultas de combinación muy complejas que SQL Server no puede manejar de manera efectiva. O, si se trata del atributo de navegación de una entidad a la que se debe acceder, este atributo es solo un subconjunto del conjunto de entidades, la carga diferida puede ser mejor que la precarga, porque la precarga cargará todos los datos Incluso si no necesita acceder a ellos. Si el rendimiento de la aplicación es extremadamente importante, será mejor que pruebe y elija el mejor entre estos dos métodos.

La carga diferida puede bloquear algunos códigos que causan problemas de rendimiento. Por ejemplo, el código no especifica la carga previa o la carga explícita, pero cuando procesa una gran cantidad de entidades y utiliza atributos de navegación en cada iteración, la eficiencia del código puede ser baja (porque habrá una gran cantidad de consultas de base de datos de ida y vuelta). Una aplicación que funciona bien en un entorno de desarrollo puede experimentar una disminución en el rendimiento de la carga diferida debido a una mayor latencia al pasar a una base de datos SQL de Windows Azure. Debe analizar y probar para asegurarse de que la carga diferida sea adecuada. Para obtener más información, consulte Desmitificar las estrategias de Entity Framework: cargar datos relacionados y usar Entity Framework para reducir la latencia de red a SQL Azure .

Deshabilitar la carga diferida antes de la serialización

Si habilita la carga diferida durante la serialización, puede terminar consultando más datos de los esperados. La serialización generalmente accede a todos los atributos de la clase. El acceso al atributo desencadena la carga diferida, y luego las entidades cargadas diferidas también se serializan. Al final, puede causar más carga diferida y serialización de atributos. Para evitar esta reacción en cadena, desactive la carga diferida antes de que la entidad se serialice.

Una forma de evitar problemas de serialización es serializar objetos de transferencia de datos (DTO) en lugar de objetos de entidad, como se muestra en el tutorial Uso de API web con Entity Framework.

Si no desea utilizar DTO, puede deshabilitar la carga diferida y evitar deshabilitar la creación de proxy para evitar problemas de proxy.

Aquí hay algunas formas de deshabilitar la carga diferida:

  • Para atributos de navegación específicos, omita la declaración de palabra clave virtual.
  • Para todas las propiedades de navegación, puede establecer LazyLoadingEnabled en falso y poner el siguiente código en el constructor de su clase de contexto:
this.Configuration.LazyLoadingEnabled = false;

Crear página del curso que muestre el nombre del departamento

La entidad del curso contiene un atributo de navegación que incluye la entidad del departamento asignada al curso. Para mostrar el nombre del departamento asignado en la lista de cursos, debe obtener la propiedad Nombre de la entidad Departamento, que es la propiedad de navegación Course.Department.

Cree un nuevo controlador "MVC 5 Controller with Views (usando Entity Framework)" para el tipo de entidad del curso y asígnele el nombre CourseController, usando la misma configuración que creó anteriormente el controlador de estudiante, como se muestra en la siguiente figura:

controlador de curso

Abra el controlador y vea el método de índice:

public ActionResult Index()
{
    var courses = db.Courses.Include(c => c.Department);
    return View(courses.ToList());
}

Verá que el andamiaje generado automáticamente utiliza el método Incluir para especificar la precarga del atributo Departamento.

Abra Views \ Course \ Index.cshtml y reemplace el original con el siguiente código:

@model IEnumerable<ContosoUniversity.Models.Course>

@{
    ViewBag.Title = "Courses";
}

<h2>Courses</h2>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table class="table">
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.CourseID)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Title)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Credits)
        </th>
        <th>
            Department
        </th>
        <th></th>
    </tr>

@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.CourseID)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Title)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Credits)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Department.Name)
        </td>
        <td>
            @Html.ActionLink("Edit", "Edit", new { id=item.CourseID }) |
            @Html.ActionLink("Details", "Details", new { id=item.CourseID }) |
            @Html.ActionLink("Delete", "Delete", new { id=item.CourseID })
        </td>
    </tr>
}

</table>

Realizó los siguientes cambios en el código de andamio:

  • Cambie el título de Índice a Curso.
  • Se agregó una línea numérica para mostrar el valor del atributo CourseID. De forma predeterminada, el andamiaje no genera plantillas de clave principal, porque generalmente no tienen sentido para el usuario final. Pero en este caso, solo estamos acostumbrados a mostrar que puede mostrarlo de esta manera.
  • Movió la fila del curso hacia la derecha y modificó su título. El andamio seleccionó correctamente el atributo Nombre de la entidad del Departamento, pero en esta página del curso, el título de la columna debe ser Departamento, no Nombre.

Tenga en cuenta que en la fila del departamento, el código de andamio muestra que la propiedad Nombre de la entidad del departamento se carga a través de la propiedad de navegación.

<td>
    @Html.DisplayFor(modelItem => item.Department.Name)
</td>

Ejecute esta página (seleccione la pestaña del curso) para ver la lista de nombres de departamento.

cursos

Cree una página de instructor para mostrar la información del curso y la inscripción.

En esta sección, creará un controlador y utilizará la vista de entidad del profesor para mostrar la página del profesor.

ics

Esta página lee y datos relacionados de las siguientes maneras:

  • La lista de instructores muestra los datos relevantes de la entidad OfficeAssignment. Existe una relación de uno a uno o cero entre el Instructor y las entidades de OfficeAssignment, puede usar la cantidad de entidad de OfficeAssignment para precargar. Como se mencionó anteriormente, cuando necesita todos los datos relacionados de la tabla principal, la precarga es más efectiva. Aquí, desea mostrar las tareas de oficina de todos los profesores.
  • Cuando el usuario selecciona un profesor, se mostrará la entidad del curso relacionada. Existe una relación de muchos a muchos entre el instructor y las entidades del curso. También puede usar la precarga en entidades del curso y sus entidades de departamento relacionadas. Pero aquí, la carga diferida puede ser más efectiva, porque solo necesita la información del curso del instructor seleccionado. De hecho, aquí se muestra cómo usar la carga diferida para cargar las propiedades de navegación entre las propiedades de navegación.
  • Cuando el usuario selecciona un curso, se muestran los datos relevantes en el registro de la entidad registrada. El curso y las entidades de inscripción están en una relación de uno a muchos. Agregará la carga explícita a las entidades de inscripción y sus entidades de estudiante relacionadas. (La carga explícita es realmente innecesaria, pero aquí solo se muestra cómo realizar una carga explícita)

Crear ViewModel para la vista indizada del instructor

La página del instructor muestra tres tablas diferentes, por lo que creará un modelo de vista con tres atributos, cada uno de los cuales contiene los datos requeridos por una tabla.

En la carpeta ViewModels, cree InstructorIndexData.cs y reemplace el original con el siguiente código:

using System.Collections.Generic;
using ContosoUniversity.Models;

namespace ContosoUniversity.ViewModels
{
    public class InstructorIndexData
    {
        public IEnumerable<Instructor> Instructors { get; set; }
        public IEnumerable<Course> Courses { get; set; }
        public IEnumerable<Enrollment> Enrollments { get; set; }
    }
}

Crear controladores y vistas de instructor

Al igual que el controlador CourseController anterior, cree un controlador InstructorController, como se muestra a continuación:

instructorcontrolador

Abra Controller \ InstructorController.cs y agregue la referencia de espacio de nombres de ViewModels:

using ContosoUniversity.ViewModels;

En el código del método Index creado por el andamio, solo la propiedad de navegación OfficeAssignment está precargada.

public ActionResult Index()
{
    var instructors = db.Instructors.Include(i => i.OfficeAssignment);
    return View(instructors.ToList());
}

Use el siguiente código para reemplazar el método Index para cargar otros datos relevantes:

public ActionResult Index(int? id, int? courseID)
{
    var viewModel = new InstructorIndexData();
    viewModel.Instructors = db.Instructors
        .Include(i => i.OfficeAssignment)
        .Include(i => i.Courses.Select(c => c.Department))
        .OrderBy(i => i.LastName);

    if (id != null)
    {
        ViewBag.InstructorID = id.Value;
        viewModel.Courses = viewModel.Instructors.Where(
            i => i.ID == id.Value).Single().Courses;
    }

    if (courseID != null)
    {
        ViewBag.CourseID = courseID.Value;
        viewModel.Enrollments = viewModel.Courses.Where(
            x => x.CourseID == courseID).Single().Enrollments;
    }

    return View(viewModel);
}

Este método recibe un parámetro de enrutamiento opcional (id) y un parámetro de cadena de consulta (courseID) para proporcionar el valor de ID del profesor y curso seleccionados y pasa todos los datos necesarios a la vista. El hipervínculo de selección en la página proporcionará estos parámetros.

El código primero crea una instancia del modelo de vista y coloca la lista de profesores en el modelo.El código especifica el uso de precarga en las propiedades de navegación OfficeAssignment y Courses.

var viewModel = new InstructorIndexData();
viewModel.Instructors = db.Instructors
    .Include(i => i.OfficeAssignment)
    .Include(i => i.Courses.Select(c => c.Department))
     .OrderBy(i => i.LastName);

El segundo método Incluir carga cursos y precarga las propiedades de navegación del Departamento para cada curso.

.Include(i => i.Courses.Select(c => c.Department))

Como se mencionó anteriormente, a menos que sea para mejorar el rendimiento, la precarga no es necesaria. Dado que las vistas siempre requieren entidades de OfficeAssgnment, es más efectivo procesarlas en la misma consulta. Las entidades del curso deben cargarse cuando se selecciona un profesor. Solo cuando la página muestra el curso con más frecuencia que sin selección, la carga previa es mejor que la carga retrasada.

Si se selecciona una ID de profesor, el profesor seleccionado se recuperará de la lista de modelos de vista. La propiedad Cursos del modelo de vista carga la entidad del curso relacionada a través de la propiedad de navegación Cursos del instructor.

if (id != null)
{
    ViewBag.InstructorID = id.Value;
    viewModel.Courses = viewModel.Instructors.Where(i => i.ID == id.Value).Single().Courses;
}

El método Where devuelve una colección pero aquí solo devuelve una entidad de conferenciante. El método Single convierte la colección en una entidad de conferenciante, lo que le permite acceder a la propiedad Cursos de la entidad.

Cuando sepa que la colección contendrá solo un elemento, puede usar el método Single en la colección. Se debe generar una excepción cuando se llama al método Single en una colección vacía o en una colección con varios elementos. Otra opción es usar SingleOrDefault, si la colección está vacía, devuelve un valor predeterminado. Pero el uso de SingleOrDefault en este ejemplo seguirá causando una excepción (intentará acceder a la propiedad Courses, pero la propiedad es una referencia nula) y el mensaje de excepción lo indicará. Al llamar al método Single, también puede pasar una condición Where en lugar de llamar a los métodos Where y Single por separado:

.Single(i => i.ID == id.Value)

En lugar de

.Where(I => i.ID == id.Value).Single()

A continuación, si se selecciona un curso, el curso seleccionado se recuperará de la lista de cursos del modelo de vista. Luego lea la entidad de registro de la propiedad de navegación de registro del curso y cárguela en la propiedad de registro del modelo de vista.

    if (courseID != null)
    {
        ViewBag.CourseID = courseID.Value;
        viewModel.Enrollments = viewModel.Courses.Where(
            x => x.CourseID == courseID).Single().Enrollments;
    }

Modificar la vista de índice del profesor

En Views \ Instructor \ Index.cshtml, reemplace el original con el siguiente código:

@model ContosoUniversity.ViewModels.InstructorIndexData

@{
    ViewBag.Title = "Instructors";
}

<h2>Instructors</h2>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table class="table">
    <tr>
        <th>Last Name</th>
        <th>First Name</th>
        <th>Hire Date</th>
        <th>Office</th>
        <th></th>
    </tr>

    @foreach (var item in Model.Instructors)
    {
        string selectedRow = "";
        if (item.ID == ViewBag.InstructorID)
        {
            selectedRow = "success";
        }
        <tr class="@selectedRow">
            <td>
                @Html.DisplayFor(modelItem => item.LastName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.FirstMidName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.HireDate)
            </td>
            <td>
                @if (item.OfficeAssignment != null)
                {
                    @item.OfficeAssignment.Location
                }
            </td>
            <td>
                @Html.ActionLink("Select", "Index", new { id = item.ID }) |
                @Html.ActionLink("Edit", "Edit", new { id = item.ID }) |
                @Html.ActionLink("Details", "Details", new { id = item.ID }) |
                @Html.ActionLink("Delete", "Delete", new { id = item.ID })
            </td>
        </tr>
    }

    </table>

Realizó los siguientes cambios en el código:

  • Cambie la clase de modelo de la vista a InstructorIndexData
  • Título cambiado
  • Se agregó una columna de oficina para mostrar la ubicación cuando el elemento. OfficeAssignent no está vacío (porque esta es una relación uno a uno o cero)
<td> 
    @if (item.OfficeAssignment != null) 
    { 
        @item.OfficeAssignment.Location  
    } 
</td> 
  • Agregue dinámicamente class = "success" al elemento tr del maestro seleccionado a través del código. Aquí, el color de fondo de la fila seleccionada se establece utilizando la hoja de estilo Bootstrap
string selectedRow = ""; 
if (item.InstructorID == ViewBag.InstructorID) 
{ 
    selectedRow = "success"; 
} 
<tr class="@selectedRow" valign="top"> 
  • Agregue un nuevo ActionLink para enviar la ID del profesor seleccionado al método Index.

Ejecute la aplicación y seleccione la pestaña Instructor. La información del instructor se muestra en la página, así como la propiedad Ubicación cuando la entidad OfficeAssignment no está vacía. Si no hay una oficina relacionada, no se muestra nada.

índice de instructores

En el archivo Views \ Instructor \ Index.cshtml, agregue el siguiente código después del elemento de la tabla para mostrar la lista de cursos del profesor seleccionado

@if (Model.Courses != null)
{
    <h3>Courses Taught by Selected Instructor</h3>
    <table class="table">
        <tr>
            <th></th>
            <th>Number</th>
            <th>Title</th>
            <th>Department</th>
        </tr>

        @foreach (var item in Model.Courses)
        {
            string selectedRow = "";
            if (item.CourseID == ViewBag.CourseID)
            {
                selectedRow = "success";
            }
            <tr class="@selectedRow">
                <td>
                    @Html.ActionLink("Select", "Index", new { courseID = item.CourseID })
                </td>
                <td>
                    @item.CourseID
                </td>
                <td>
                    @item.Title
                </td>
                <td>
                    @item.Department.Name
                </td>
            </tr>
        }

    </table>
}

Este código se usa para leer y mostrar los atributos del curso en el módulo de vista. También proporciona un hipervínculo Seleccionar para enviar la ID del curso seleccionado al método Índice.

Ejecute la página y seleccione un profesor. Verá una tabla que muestra los cursos asignados a ese profesor.

ic

Agregue el siguiente código después del código que acaba de agregar para mostrar la lista de estudiantes inscritos en el curso seleccionado.

@if (Model.Enrollments != null)
{
    <h3>
        Students Enrolled in Selected Course
    </h3>
    <table class="table">
        <tr>
            <th>Name</th>
            <th>Grade</th>
        </tr>
        @foreach (var item in Model.Enrollments)
        {
            <tr>
                <td>
                    @item.Student.FullName
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Grade)
                </td>
            </tr>
        }
    </table>
}

Este código lee la propiedad Inscripciones del modelo de vista y se usa para mostrar a los estudiantes que se registran para el curso.

Ejecute la página y seleccione un profesor, luego seleccione un curso para ver los estudiantes registrados y sus calificaciones.

ics

Agregar carga explícita

Abra InstructorController.cs y verifique cómo el método Index obtiene la lista registrada para el curso seleccionado:

    if (courseID != null)
    {
        ViewBag.CourseID = courseID.Value;
        viewModel.Enrollments = viewModel.Courses.Where(
            x => x.CourseID == courseID).Single().Enrollments;
    }

Cuando recuperó la lista de profesores, especificó la precarga en el atributo de navegación Cursos y el atributo de departamento de cada curso. Luego coloca la colección del curso en el modelo de vista y ahora puede acceder a ella registrando las propiedades de navegación en la entidad de la colección. Debido a que no especificó la precarga de la propiedad de navegación Navigation.Enrollments, los datos de esta propiedad usarán carga diferida y solo se cargarán cuando se muestre la página.

Si deshabilita la carga diferida sin cambiar otro código, la propiedad Inscripciones estará vacía, sin importar cuántos registros se realicen realmente. En este caso, si desea cargar el atributo Inscripciones, debe especificar la carga previa o la carga explícita. Ya has visto cómo usar la precarga. Para demostrar la carga explícita, reemplace la parte original del alumno con el siguiente código, utilizaremos la carga explícita en la propiedad Inscripciones.

public ActionResult Index(int? id, int? courseID)
{
    var viewModel = new InstructorIndexData();

    viewModel.Instructors = db.Instructors
        .Include(i => i.OfficeAssignment)
        .Include(i => i.Courses.Select(c => c.Department))
        .OrderBy(i => i.LastName);

    if (id != null)
    {
        ViewBag.InstructorID = id.Value;
        viewModel.Courses = viewModel.Instructors.Where(
            i => i.ID == id.Value).Single().Courses;
    }

    if (courseID != null)
    {
        ViewBag.CourseID = courseID.Value;
        // Lazy loading
        //viewModel.Enrollments = viewModel.Courses.Where(
        //    x => x.CourseID == courseID).Single().Enrollments;
        // Explicit loading
        var selectedCourse = viewModel.Courses.Where(x => x.CourseID == courseID).Single();
        db.Entry(selectedCourse).Collection(x => x.Enrollments).Load();
        foreach (Enrollment enrollment in selectedCourse.Enrollments)
        {
            db.Entry(enrollment).Reference(x => x.Student).Load();
        }

        viewModel.Enrollments = selectedCourse.Enrollments;
    }

    return View(viewModel);
}

Después de seleccionar la entidad del curso, el nuevo código utiliza la propiedad de navegación Inscripciones del curso cargado explícitamente:

db.Entry(selectedCourse).Collection(x => x.Enrollments).Load();

Luego cargue explícitamente la entidad Alumno asociada con cada entidad de Inscripción:

db.Entry(enrollment).Reference(x => x.Student).Load();

Tenga en cuenta que usa el método Colección para cargar la colección, pero para los atributos con una sola entidad, use el método Referencia para cargar.

Ahora vuelva a ejecutar la página y confirme que todo funciona correctamente, pero de hecho ha cambiado la forma en que se recuperan los datos.

Resumen

Ahora ha intentado cargar los datos relevantes en las propiedades de navegación utilizando métodos de carga diferidos, precargados y explícitos. En el siguiente tutorial, aprenderá cómo actualizar los datos relevantes.

Información del autor

tom-dykstra Tom Dykstra -Tom Dykstra es un programador senior y escritor en el equipo de Microsoft Web Platform and Tools.

Publicó 40 artículos originales · 25 alabanzas · 100,000+ vistas

Supongo que te gusta

Origin blog.csdn.net/yym373872996/article/details/53113785
Recomendado
Clasificación