0

I know there are question and answers with that particular problem here on so, but my problem is little unique (I guess).

Here is my model class:

public class RoleMaster
{
    [Key]
    [Required]
    public int Id { get; set; }

    [Required]
    [StringLength(20)]        
    public string Role { get; set; }
}

Here is my controller:

public class RolesController : ControllerBase
{
    private readonly IRolesRepository _repo;


    public RolesController(IRolesRepository repo)
    {
        _repo = repo;
    }


    [HttpGet]
    public IActionResult Get()
    {
        var roles = _repo.GetRoles();
        return new JsonResult(roles);
    }
}

My repository:

public interface IRolesRepository
{
    Task<IEnumerable<RoleMaster>> GetRoles();
}

and here is the GetRoles method:

public async Task<IEnumerable<RoleMaster>> GetRoles()
    {
        try
        {
            var roles = await db.RoleMaster.AsNoTracking().ToListAsync();

            return roles;
        }
        catch (Exception)
        {
            throw;
        }
    }

and this is the error I am getting:

An unhandled exception occurred while processing the request. 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 the maximum allowed depth of 32. System.Text.Json.ThrowHelper.ThrowInvalidOperationException_SerializerCycleDetected(int maxDepth)

From other questions I found out that if you have references with other tables then this kind of error can occur but in my case there is no other table involved. This is the first table I have created and was just trying the get method.

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
Sanjay Kumar
  • 198
  • 2
  • 3
  • 12
  • Can you post what `GetRoles()` actually returns, which then causes your exception? – canton7 Jan 09 '20 at 11:54
  • Does this answer your question? [.NET CORE 3 Upgrade CORS and Json(cycle) XMLHttpRequest Error](https://stackoverflow.com/questions/57912012/net-core-3-upgrade-cors-and-jsoncycle-xmlhttprequest-error) – Sushant Yelpale Jan 09 '20 at 12:00
  • mmm then i would say there is something really wrong.. is there any more to the stack trace? – Seabizkit Jan 09 '20 at 12:43
  • 2
    I think maybe your issue after assuming wrong... is that _repo.GetRoles(); is not using `await` this `var roles = _repo.GetRoles();` should be `var roles = await _repo.GetRoles();`, your mixing async and non async, if correct ill post as answer – Seabizkit Jan 09 '20 at 12:45

3 Answers3

6

_repo.GetRoles(); is not using await keyword

this var roles = _repo.GetRoles();

should be

var roles = await _repo.GetRoles();

your mixing async and non async,

below untested but you should get the jist

  [HttpGet]
    public  async Task<IActionResult> Get()
    {
        var roles = **await** _repo.GetRoles();
        return new JsonResult(roles);
    }

Original Answer was:

FYI - my original answer was actually correct.... objectA was... Task, as that is what was being returned from _repo.GetRoles();

your objectsA have references to objectB which references objectsA.

this just end in a recursion loop, basically never end trying to serialize.

hense possible object cycle

Also you didn't include the code where exception is possible being throw JsonResult so can t not see what serializer you using.

Further if you are using newtonJson then the support for complex object is even more limited.

Further - Just an FYI based q's in on comments the non async version, as your main(controller) thread is not async if you don't want to change it, as above or for clearity

public List<RoleMaster> GetRoles()
{
    try
    {
        var roles =  db.RoleMaster.AsNoTracking().ToList();
        return roles;
    }
    catch (Exception)
    {
        throw;
    }
}

Update for more guidance

https://learn.microsoft.com/en-us/aspnet/core/web-api/action-return-types?view=aspnetcore-3.1

Seabizkit
  • 2,417
  • 2
  • 15
  • 32
  • Exception was being thrown when I am querying the database and not when I am trying to serialize the result. Control never returns to the get method and exception is thrown at database query execution i.e here `var roles = await db.RoleMaster.AsNoTracking().ToListAsync()`;. That's why I find it a little strange that it started working when I added async await in the get method. Maybe I still don't understand these things clearly. – Sanjay Kumar Jan 10 '20 at 10:15
  • As you main method is not Async and you did not await `var roles = _repo.GetRoles();` at this time with no awiat. roles is equal to `Task>` and not `IEnumerable` then when trying to serialize it is trying to serialize something which is a wrapper, which is the Task, as https://source.dot.net/#q=Task, ps thanks for the `https://source.dot.net` quite handy didnt know about it. Task action are deferred, as it was never awaited, the actual call to the db did not take place, well its result was never sync up as how you would except. – Seabizkit Jan 10 '20 at 10:33
  • Since Task action are deferred, should I remove async and await from get method? If I do that my problem will come back. Also you said that the db call never took place but when I inspected my model class with a check I found that my properties were assigned with correct values from tables therfor db call completed successfully and after properties initialization it throws said exception. – Sanjay Kumar Jan 10 '20 at 10:55
  • My wording was not 100% i was trying to paint a picture, to give you some idea of what is happening. The Tasks result is never "bubbled up"*... so remains a Task, that does not mean that the work is not being done, it will just never sync this back to the thread which you are interested in. So the answer to your question is, do you need the `result`... if yes, then you must `await`, if not then you can leave as is, in your case you want to serialize the `result` so if you want to use `async` then yes you must await, as you want to serialize the `result`. hope that makes sense to you. – Seabizkit Jan 10 '20 at 11:06
  • Okay you are correct but my problem is still there. You see it is recommended that every db call should be non blocking i.e async await so I have to keep async await with my db call function and Task actions are deferred so I don't want to use that. So what should I do ? :) – Sanjay Kumar Jan 10 '20 at 11:42
  • you need to change your controller action to use async as like the example i gave. then you can happily await everything with out issue, and that would be the correct way. – Seabizkit Jan 10 '20 at 11:47
  • No I don't have to change my controller action and neither that is correct way. I just remembered something I did a few months ago and that is the correct way.In my controller method I just have to use `.Result` like this - `var roles = _repo.GetRoles().Result;` and everything works like a charm. – Sanjay Kumar Jan 10 '20 at 11:48
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/205748/discussion-between-seabizkit-and-sanjay-kumar). – Seabizkit Jan 10 '20 at 11:50
  • @SanjayKumar `var roles = _repo.GetRoles().Result;` is blocking aka the same as non-async. I think ur a little confused, 'public IActionResult Get()' is not async so... nothing below can be async without blocking, aka you have async mixed with sync, I showed you how to change this to async. you say you dnt want to change ur controller, well then you have to use blocking calls to get the result. Which at that point its the same as change the db call to just use .Tolist() instead of ToListAsync – Seabizkit Nov 25 '22 at 04:17
1

I had the same problem.

This is what solved my problem:

Putting JsonIgnore attribute on the referencing property (which caused endless serialization)

CleanCoder
  • 2,406
  • 1
  • 10
  • 35
1

At .NET 6 you can ignore circular references by adding to your Program.cs :

using System.Text.Json.Serialization;


builder.Services.AddControllersWithViews()
    .AddJsonOptions(options => options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles);
Konstantinos
  • 109
  • 1
  • 6