Tengo los siguientes modelos de entidad configurados en mi proyecto EF Core:
public class ForumPost { [Key] public int Id { get; set; } public string Title { get; set; } // ...some other properties public List<Tag> Tags { get; set; } } public class Tag { [Key] public string Name { get; set; } public string Description { get; set; } // ...some other properties public List<ForumPost> Posts { get; set; } } Así que tengo una relación de muchos a muchos entre publicaciones y etiquetas. No tengo configuración de relación en mi OnModelCreating .
Mi contexto está configurado de la siguiente manera:
public class ForumsContext : DbContext { public DbSet<ForumPost> Posts { get; set; } public DbSet<Tag> Tags { get; set; } } Cuando consulto mi ForumsContext de la siguiente manera:
var posts = await context.Posts.Include(p => p.Tags).ToListAsync(); EF completa automáticamente el campo Posts para cada uno de los objetos Tag en cada uno de los objetos ForumPost en las posts . No puedo entender por qué sucede esto y si tuviera que serializar la lista de posts anterior en JSON, arrojaría una excepción debido al ciclo del objeto ( Post->Tags->Posts->Post ).
¿Por qué se rellenan las listas de publicaciones de la etiqueta? ¿Cómo puedo decirle a EF que no complete esta lista?
Así es como funciona Entity Framework. La llamada Include llena la propiedad de navegación en ambos lados de la relación.
Las convenciones de Entity Framework dictan que debe tener propiedades de navegación en ambos extremos de una relación de varios a varios.
Tampoco parece posible tener solo una propiedad de navegación usando la API fluida. El siguiente código no funciona, y al preguntar si hay una manera de evitar definir la propiedad para una de las entidades, en este hilo el equipo respondió "Todavía no" .
modelBuilder.Entity<ForumPost>().HasMany<Tag>(post => post.Tags).WithMany();En cuanto a los errores de ciclo de objetos al serializar a JSON, estos pueden manejarse o ignorarse en .NET 5 o posterior.
Este artículo entra en más detalles sobre el problema y muestra una solución para NewtonSoft.Json.
No soy fanático de esta solución. Terminará con propiedades null donde el serializador se detuvo porque encontró un ciclo.
Pero todo eso no viene al caso. No necesita cambiar su modelo de datos o la configuración del serializador JSON para solucionar este problema. El problema es que estás exponiendo tu modelo de datos en primer lugar.
La solución adecuada es asignar sus entidades a modelos de respuesta/vista o DTO en su lugar. Esto le permite dar formato a las respuestas adecuadas y permite que su modelo de datos evolucione sin interferir con su modelo de respuesta.
Ejemplo:
public class ForumPostResponse { public int Id { get; set; } public string Title { get; set; } public List<Tag> Tags { get; set; } } public class TagResponse { public string Name { get; set; } public string Description { get; set; } } También puede formatear ForumPostResponse.Tags como una lista de string , en caso de que solo desee una lista de nombres de etiquetas en las publicaciones del foro e incluir la descripción en otras respuestas. Todo es posible.
Necesita escribir código adicional para manejar el mapeo entre objetos, pero es una pequeña compensación por un diseño mucho más agradable y fácil de mantener. Y además, existen varias librerías que simplifican el proceso, siendo la más popular AutoMapper .
Sé que esta podría no ser la mejor solución para la referencia circular sobre la serialización, pero una solución alternativa puede ser inicializar la propiedad de la lista:
public class ForumPost { [Key] public int Id { get; set; } public string Title { get; set; } // ...some other properties public List<Tag> Tags { get; set; } = new List<Tag>(); } public class Tag { [Key] public string Name { get; set; } public string Description { get; set; } // ...some other properties public List<ForumPost> Posts { get; set; } = new List<ForumPost>(); }Además, debido a la inicialización, puede manipular esas listas sin preocuparse por las excepciones nulas y todo eso. Pero tenga en cuenta que podría haber una mejor solución para este problema.