При написании запроса с Include() на c#, возникает проблема на фронте (JS) с Fetch API
Изучаю Web Api. Бэк на c#, на фронте использую JS с Fetch API. Пример неработающего кода с Include():
[HttpGet("{id}")]
public async Task<ActionResult<Student>> GetStudent(int id)
{
var student = await _context.Students.Include(x => x.Group).FirstOrDefaultAsync(x => x.Id == id);
if (student == null)
return BadRequest();
return new ObjectResult(student);
}
и JS :
async function GetStudent(id) {
fetch("/api/student/" + id)
.then(response => response.json())
.then(data => console.log(data));
}
Вот что выдает Postman при запросе

Отношение классов = Group - Student: Один ко многим.
Почему не работает?
Как я понял, нужно сериализовать данные Group, но я не знаю на каком этапе.
Сущность Student:
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Group Group { get; set; }
public List<IndividalLesson> IndividalLessons { get; set; } = new List<IndividalLesson>();
Group:
public int id { get; set; }
public string GroupName { get; set; }
public ClassTeacher ClassTeacher { get; set; }
public List<Student> Students { get; set; } = new List<Student>();
public List<Lesson> Lessons { get; set; } = new List<Lesson>();
P.S. Без Include() все работает прекрасно
Ответы (1 шт):
Это перевод ответа отсюда: JSON.NET Error Self referencing loop detected for type.
А тот, в свою очередь, перепечатан отсюда: Loop Reference handling in Web API.
Способ 1: Глобальное игнорирование циклических ссылок
Сериализатор JSON.NET предоставляет возможность игнорировать циклические ссылки. Для этого, поместите следующий код в файл WebApiConfig.cs:
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling
= Newtonsoft.Json.ReferenceLoopHandling.Ignore;
Этот способ заставит сериализатор игнорировать цепочки ссылочных свойств, обход которых приводит к зацикливанию. Однако у него есть минусы:
- Теряется информация о циклических связях
- Работает только с JSON.NET
- Невозможно контролировать уровень вложенности, при котором цепочка свойств определяется как циклическая
Чтобы использовать этот способ в обычном ASP.NET проекте, добавьте указанную выше строку в Global.asax.cs, но сначала добавьте это:
var config = GlobalConfiguration.Configuration;
Чтобы использовать этот способ в проекте .Net Core, измените Startup.cs таким образом:
var mvc = services.AddMvc(options =>
{
...
})
.AddJsonOptions(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
Способ 2: Глобальное сохранение циклических ссылок
Второй способ похож на первый. Просто измените код так:
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling
= Newtonsoft.Json.ReferenceLoopHandling.Serialize;
config.Formatters.JsonFormatter.SerializerSettings.PreserveReferencesHandling
= Newtonsoft.Json.PreserveReferencesHandling.Objects;
После применения этого способа, структура данных изменится таким образом:
[
{
"$id":"1",
"Category":{
"$id":"2",
"Products":[
{
"$id":"3",
"Category":{
"$ref":"2"
},
"Id":2,
"Name":"Yogurt"
},
{
"$ref":"1"
}
],
"Id":1,
"Name":"Diary"
},
"Id":1,
"Name":"Whole Milk"
},
{
"$ref":"3"
}
]
Ключевые слова $Id и $ref позволяют сохранить все ссылки оставляя граф объекта плоским. Но клиентский код должен понимать такую структуру JSON объекта. Это решение также применимо только к сериализатору JSON.NET.
Способ 3: Игнорирование или сохранение циклических ссылок при помощи атрибутов
Этот способ заключается в том, чтобы добавить в сериализуемый класс атрибуты для управления поведением сериализации на уровне класса или свойства.
Чтобы игнорировать свойство:
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
[JsonIgnore]
[IgnoreDataMember]
public virtual ICollection<Product> Products { get; set; }
}
Атрибут JsonIgnore предназначен для JSON.NET, а IgnoreDataMember - для XmlDCSerializer.
Чтобы сохранить ссылку:
// Fix 3
[JsonObject(IsReference = true)]
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
// Fix 3
//[JsonIgnore]
//[IgnoreDataMember]
public virtual ICollection<Product> Products { get; set; }
}
[DataContract(IsReference = true)]
public class Product
{
[Key]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public virtual Category Category { get; set; }
}
Атрибут JsonObject(IsReference = true)] предназначен для JSON.NET, а [DataContract](IsReference = true)] предназначен для XmlDCSerializer. Обратите внимание: после добавления DataContract к классу вам необходимо добавить DataMember к свойствам, которые вы хотите сериализовать.
Атрибуты могут применяться как c сериализатором JSON.NET, так и c XML-сериализатором и предоставляют больше возможностей для управления сериализацией.