Línea de fondo
Su código ha recuperado datos (entidades) a través del marco de entidad con la carga lenta habilitada y después de que se haya eliminado el DbContext, su código hace referencia a propiedades (entidades relacionadas / de relación / navegación) que no se solicitaron explícitamente.
Más específicamente
El InvalidOperationException
con este mensaje siempre significa lo mismo: está solicitando datos (entidades) de entity-framework después de que se haya eliminado el DbContext.
Un caso simple:
(estas clases se usarán para todos los ejemplos en esta respuesta, y suponen que todas las propiedades de navegación se han configurado correctamente y tienen tablas asociadas en la base de datos)
public class Person
{
public int Id { get; set; }
public string name { get; set; }
public int? PetId { get; set; }
public Pet Pet { get; set; }
}
public class Pet
{
public string name { get; set; }
}
using (var db = new dbContext())
{
var person = db.Persons.FirstOrDefaultAsync(p => p.id == 1);
}
Console.WriteLine(person.Pet.Name);
La última línea arrojará el InvalidOperationException
porque dbContext no ha deshabilitado la carga diferida y el código está accediendo a la propiedad de navegación de Mascotas después de que el Contexto haya sido eliminado por la instrucción de uso.
Depuración
¿Cómo encuentra la fuente de esta excepción? Además de mirar la excepción en sí, que se lanzará exactamente en el lugar donde ocurre, se aplican las reglas generales de depuración en Visual Studio: coloque puntos de interrupción estratégicos e inspeccione sus variables , ya sea pasando el mouse sobre sus nombres, abriendo un ( Rápido) Mire la ventana o use los diversos paneles de depuración como Locales y Autos.
Si desea averiguar dónde se establece o no la referencia, haga clic con el botón derecho en su nombre y seleccione "Buscar todas las referencias". Luego puede colocar un punto de interrupción en cada ubicación que solicite datos y ejecutar su programa con el depurador adjunto. Cada vez que el depurador se interrumpe en dicho punto de interrupción, debe determinar si su propiedad de navegación debería haberse rellenado o si los datos solicitados son necesarios.
Formas de evitar
Deshabilitar carga diferida
public class MyDbContext : DbContext
{
public MyDbContext()
{
this.Configuration.LazyLoadingEnabled = false;
}
}
Pros: en lugar de lanzar la InvalidOperationException, la propiedad será nula. Acceder a propiedades de nulo o intentar cambiar las propiedades de esta propiedad generará una NullReferenceException .
Cómo solicitar explícitamente el objeto cuando sea necesario:
using (var db = new dbContext())
{
var person = db.Persons
.Include(p => p.Pet)
.FirstOrDefaultAsync(p => p.id == 1);
}
Console.WriteLine(person.Pet.Name); // No Exception Thrown
En el ejemplo anterior, Entity Framework materializará la mascota además de la persona. Esto puede ser ventajoso porque es una sola llamada a la base de datos. (Sin embargo, también puede haber grandes problemas de rendimiento dependiendo de la cantidad de resultados devueltos y la cantidad de propiedades de navegación solicitadas, en este caso, no habría penalización de rendimiento porque ambas instancias son solo un registro y una combinación).
o
using (var db = new dbContext())
{
var person = db.Persons.FirstOrDefaultAsync(p => p.id == 1);
var pet = db.Pets.FirstOrDefaultAsync(p => p.id == person.PetId);
}
Console.WriteLine(person.Pet.Name); // No Exception Thrown
En el ejemplo anterior, Entity Framework materializará la mascota independientemente de la persona al hacer una llamada adicional a la base de datos. De forma predeterminada, Entity Framework rastrea los objetos que ha recuperado de la base de datos y, si encuentra propiedades de navegación que coinciden, se rellenará automáticamente estas entidades. En este caso, debido a que el PetId
en el Person
objeto coincide con el Pet.Id
, Entity Framework asignará el Person.Pet
que el Pet
valor recuperado, antes de que se le asigna el valor de la variable de mascota.
Siempre recomiendo este enfoque, ya que obliga a los programadores a comprender cuándo y cómo el código solicita datos a través de Entity Framework. Cuando el código arroja una excepción de referencia nula en una propiedad de una entidad, casi siempre puede estar seguro de que no ha solicitado explícitamente esos datos.
query.Include("SomeOtherTable")
db.MemberLoans.Include("LoanProduct").OrderByDescending()
verificar la sintaxis porque no tengo VS frente a mí.db.MemberLoans.Include("LoanProduct").Include("SomeOtherTable)
. Compruebe las respuestas de @Tragedian y @lazyberezovsky