0

I have encountered really weird situation. I am developing a net 5 api, and (among ohther entities) have three tables, Doctor, Specialization, and DoctorSpecialization. My entities:

public class Doctor
{
    public int Id { get; set; }           
    public string Name { get; set; }
    public string Resume { get; set; }
    public ICollection<DoctorSpecialization1> DoctorSpecializations { get; set; }       
}

public class DoctorSpecialization
{
    public int Id { get; set; } 
    public int Doctor1Id { get; set; }
    [ForeignKey("Doctor1Id")]
    public Doctor1 Doctor { get; set; }

    public int Specialization1Id { get; set; }
    [ForeignKey("Specialization1Id")]
    public Specialization1 Specialization { get; set; }
}

public class Specialization
{
    public int Id { get; set; } 
    public string SpecializationName { get; set; }
}

I want to fetch all the specializations associated with the certain doctor, therefore I created a service:

public class DoctorService : IDoctorService
{
    public async Task<List<Specialization>> GetAllSpecializationsForDoctor(int id)
    {
        var doctor = await _context.Doctors.Where(x => x.Id == id).FirstOrDefaultAsync();

        var doctorSpecializations = await _context.DoctorSpecializations.
                                    Where(x => x.DoctorId == doctor.Id)
                                   .ToListAsync();
        
        IEnumerable<int> ids = doctorSpecializations.Select(x => x.SpecializationId);

        var specializations = await _context.Specializations.Where(x => 
        ids.Contains(x.Id)).ToListAsync();
        return specializations;
    }     
}

In the end, I am adding a method in my controller which is supposed to fetch specializations based on doctor's id:

[HttpGet("specializations/{id}")]
public async Task<ActionResult<List<Specialization1>>> GetSpecializationsForDoctor(int id)
{
    var doctor = await _doctorService.FindDoctorById(id);

    if (doctor == null) return NotFound();

    var specialization = _doctorService.GetAllSpecializationsForDoctor(id);
    
    return Ok(specialization);
}

When I run this in postman, I get the error stated in the title of this question. However, I encountered an article that explains I should install newtonsoft.json and make some changes in my Startup in order to overcome this issue. So therefore I installed Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="5.0.10" and made changes in my Starup as follows:

 services.AddControllers().AddNewtonsoftJson(options =>
        options.SerializerSettings.ReferenceLoopHandling = 
 Newtonsoft.Json.ReferenceLoopHandling.Ignore);

This time I get the expected result, but my postman shows tons of data before giving me the wanted result, and I wonder is it ok to return this much data to the client. Can somebody explain me what is going on, tnx in advance!

EKOlog
  • 410
  • 7
  • 19
Petar Sardelic
  • 69
  • 1
  • 2
  • 7
  • Why do your entity names have a number on when used in a prop but not when defined? – Caius Jard Oct 15 '21 at 20:36
  • soprry, I corrected that, I was doing all sorts of testing in my project – Petar Sardelic Oct 15 '21 at 20:45
  • It sounds like your serializer is getting caught in some cycle of trying to serialize eg a Doctor (let's call him Jekyll) who has a list of DoctorSpecializations which each have a Doctor, which is Doctor Jekyll, who has a list of DoctorSpecializations which each have a Doctor, which is Doctor Jekyll, who has a list of DoctorSpecializations which each have a Doctor, which is Doctor Jekyll, who .. – Caius Jard Oct 15 '21 at 20:50
  • 1
    Don't serialize the db entities EF made for you because it implicitly creates cycles in its graph when it wires up relationships. Map to DTOs instead that have a finite reach/are only singly directionally linked – Caius Jard Oct 15 '21 at 20:51
  • what do you suggest, should I rewrite the query? – Petar Sardelic Oct 15 '21 at 21:36
  • 1
    I'm not sure I have any suggestion further to what I noted above; I've never encountered this because I don't send my DB entities for ser to the front end - I have a different set of objects, call them Dto or ViewModel or whatever, that encapsulate just the data the front end is interested in, and the graph is acyclic; my DoctorDto would have a List of SpecialismDto but a Specialism wouldn't link back to a Doctor. Libraries like AutoMapper, AgileMapper, Mapster can create and populate these DTOs for you so it's no bother to have them, ans they can massively cut down on the data transited – Caius Jard Oct 16 '21 at 03:47
  • tnx for suggestion! – Petar Sardelic Oct 16 '21 at 07:47

2 Answers2

0

Apparently the solution was so simple, I missed the await in:

        var specialization = await 
 _doctorService.GetAllSpecializationsForDoctor(id);

Once again I managed to find solution thanks to stackoverflow, because apparently someone else had this problem in: JsonException: A possible object cycle was detected which is not supported. This can either be due to a cycle or if the object depth is larger than

So please everyone, don't be so superficial as I was, make sure to use await and save your time. Thanks again to the community:).

Petar Sardelic
  • 69
  • 1
  • 2
  • 7
0

You should not return entities from your controllers. Instead make use of DTOs. I had the same issue and it was related to returning the entire entity with it relations.