2011-12-20 7 views
7

Muchos de los errores que he estado solucionando últimamente son el resultado de referencias nulas al acceder a las propiedades de navegación de objetos cargados utilizando entity framework. Creo que debe haber un error en la forma en que estoy diseñando mis métodos. Aquí hay un ejemplo ...Evite NullReferenceException al acceder a EF Navigation Properties

Una tarea contiene muchos roles, cada rol hace referencia a un usuario.

public class Role 
{ 
    public int Id; 
    public int User_Id; 
    public string Type; 
} 

public class User 
{ 
    public int Id 
    public string Name; 
}  

public class Task 
{ 
    public int Id; 
    public string Name; 
    public string Status; 
    public List<Role> Roles; 
} 

Teniendo en cuenta que yo hubiera preguntado mi contexto como éste por error y no se ha cargado usuario ...

var task = context.Tasks.Include(x=>x.Roles).FirstOrDefault; 

Y entonces llamar a este método ...

public void PrintTask(Task task) 
{ 
    Console.WriteLine(task.Name); 
    Console.WriteLine(task.Status); 

    foreach(var r in task.Roles) 
    { 
     Console.WriteLine(r.User.Name); //This will throw NRE because User wasn't loaded 
    } 
} 

Es posible que haya creado este método con todas las intenciones de cargar Roles y Usuario, pero la próxima vez que lo use puedo olvidar que necesito ambos. Idealmente, la definición del método debería decirme qué datos son necesarios, pero incluso si paso tanto en Tarea como en Roles, todavía faltan Roles-> Usuario.

¿Cuál es la forma correcta de referenciar estas relaciones y asegúrese de que estén cargadas en algo así como este método de impresión? Me interesa un mejor diseño, por lo que "Usar lazy Loading" no es la respuesta que estoy buscando.

Gracias!

EDIT:

Sé que puedo cargar la tarea como esta ...

var task = context.Tasks.Include(x=>x.Roles.Select(z=>z.User)).FirstOrDefault(); 

Lo que yo quiero saber es ¿Cómo diseño mi método para que cuando vuelva y utilizarlo Dentro de 6 meses, ¿sé qué datos deben cargarse en mis entidades? La definición del método no indica qué es necesario para usarlo. O cómo bloquear contra estas referencias nulas. Tiene que haber un mejor diseño.

+0

posible duplicado de [ ¿Qué es una NullReferenceException en .NET?] (Http://stackoverflow.com/questions/4660142/what-is-a-nullreferenceexception-in-net) –

+0

Esto no es diferente de cualquier otro problema 'NullReferenceException'. –

Respuesta

1

Muy buena pregunta. Aquí hay algunas soluciones posibles que, si bien no obligan a evitar las NRE, le darán pistas a la persona que llama que necesitan Include cosas:

La primera opción es no tener acceso a su método como no- propiedad garantizada de una entidad; más bien, obligar a la persona que llama a aprobar ambas entidades:

public void PrintTask(Task task, User taskUser) 
{ 
    // ... 
} 

Otra opción es nombrar el parámetro del método tal que así será clave la persona que llama en cuanto a lo que se requiere:

public void PrintTask(Task taskWithUser) 
{ 
    // ... 
} 
+0

Gracias. Creo que has validado mis pensamientos de que no había una solución pura para esto. Intento mantener el acceso a la propiedad lo más simple posible, y pasarlos individualmente cuando sea posible. – BZink

2

Puede usar el método de extensión Select para cargar ansiosamente Users.

var task = context.Tasks.Include(x => x.Roles) 
      .Include(x => x.Roles.Select(r => r.User)) 
      .FirstOrDefault(); 

Editar:

Hay algunas maneras que se me ocurre para evitar la NRE

  • Prueba de integración utilizando una base de datos SQL Server CE/Express. La prueba unitaria con contextos falsos no funcionará correctamente.
  • Cargando las entidades cercanas a donde se consumen. De modo que Include s están cerca de donde se usan las entidades.
  • Pasando DTO/ViewModels a las capas superiores sin pasar las entidades .
+0

Lo siento, no dejé en claro que sabía cómo cargar al usuario. Y, de hecho, puede hacer eso en una declaración Include, no dos. Lo que estoy tratando es usar métodos donde no es evidente qué referencias son necesarias. Entonces, si cargo incorrectamente la entidad y uso el método, obtengo la referencia nula. – BZink

+0

@BZink respuesta actualizada – Eranga

1

User deben cargarse con pereza en su nota bucle acaba sin embargo que esto es un problema clásico select N + 1 que se debe fijar con otra Include.

creo que la raíz del problema es que este particular, ya sea Role hace no tiene un User, o que este en particular Role 's tiene User conjunto nulo por su Name. Necesitará marcar ambos para null en su bucle

foreach(var r in task.Roles) 
{ 
    if (r.User != null) 
     Console.WriteLine(r.User.Name ?? "Name is null"); 
} 
Cuestiones relacionadas