【问题标题】:JSON response does not include all itemsJSON 响应不包括所有项目
【发布时间】:2016-08-09 15:57:28
【问题描述】:

由于某种原因,只有每个数组的第一项作为 JSON 返回,有什么线索吗?

这是我在调试过程中看到的,如您所知,“类别”中有两个项目,“任务”中有两个项目:

Postman JSON 结果(它应该返回所有项目,不是吗?):

作为参考,这是我的“Category.cs”:

public class Category
{
    public int CategoryId { get; set; }
    public string Name { get; set; }
    public DateTime Timestamp { get; set; }
    public string Username { get; set; }

    public ApplicationUser ApplicationUser { get; set; }

    public virtual ICollection<Task> Tasks { get; set; }
}

我的“Task.cs”:

public class Task
{
    public int TaskId { get; set; }
    public string Name { get; set; }
    public DateTime Timestamp { get; set; }

    public virtual Category Category { get; set; }
}

还有我的 API:

    [HttpGet]
    public JsonResult Get()
    {
        var result = _repo.GetAllForUser("lucas@test.com");

        return Json(result);
    }

和存储库:

    public IEnumerable<Category> GetAllForUser(string name)
    {
        return _ctx.Categories
                    .Where(c => c.ApplicationUser.UserName == name)
                    .Include(c => c.Tasks)
                    .ToList();           
    }

这是我插入数据库的内容,以及我应该从 Api 中检索的内容:

        categories.Add(new Category
        {
            Name = "cat 1",
            Tasks = new List<Task>
            {
                new Task { Name="task 1" },
                new Task { Name="task 2" }
            }
        });
        categories.Add(new Category
        {
            Name = "cat 2",
            Tasks = new List<Task>
            {
                new Task { Name="task 3" },
                new Task { Name="task 4" }
            }
        });

【问题讨论】:

  • 您能分享一下您的Task 模型的样子吗?
  • 提供更多相关代码
  • 通过添加更多相关代码编辑问题
  • @mic4ael: 没有 Json 类,它是来自控制器的 Json 方法,它是带有状态码的新 JsonObjectResult 的快捷方式
  • 您是否在 Windows 上运行您的应用程序?根据您的模型,您似乎有循环引用(任务引用类别,反之亦然),这会导致 Json.net 抛出异常:github.com/aspnet/IISIntegration/issues/39 ...试试这个:启用日志记录并查看您是否看到任何正在记录的异常。 ..还可以尝试非 IIS 场景(例如:直接在控制台中执行 dotnet run 并启用控制台日志记录)

标签: c# .net json api asp.net-core


【解决方案1】:

正如 Kiran 指出的,您的模型中有循环引用,这会导致异常。 This bug 错误地使其 看起来 像请求正在完成部分数据。 (循环引用是Category -> Tasks -> Task -> Category)

实际发生的是响应的 JSON 序列化过程中未处理的异常。 ASP.NET Core 并没有中止连接(应该如此),而是发回在错误发生之前被序列化的所有内容。

您可以定义一个不包含从Task 回到Category 的引用的 DTO 类,或者返回一个匿名类型:

[HttpGet]
public JsonResult Get()
{
    var result = _repo.GetAllForUser("lucas@test.com");

    var response = new {
        categoryId: result.CategoryId,
        name: result.Name,
        timestamp: result.Timestamp,
        username: result.Username,
        tasks: result.Tasks.Select(t => new {
            taskId: t.TaskId,
            name: t.Name,
            timestamp: t.Timestamp
        })
    };

    return Json(response);
}

如果您经常这样做,创建 DTO 类并使用 AutoMapper 等工具为您进行映射是有意义的。

【讨论】:

  • 很好的解释,不胜感激。事实上,这是循环引用,一旦我从我的 Task 类中删除了 Category 变量,它就会按预期工作。对我来说奇怪的是,这就是 EF 建议连接 Model First 类的方式。执行查询时,也很容易从一个班级导航到另一个班级。那么实际的建议是什么,我应该在创建我的 Model First 类时阻止循环引用,或者我应该从我的 Api 返回一个匿名类型,就像您在上面指定的那样。谢谢
  • @lucas 直接返回实体模型作为响应通常不是一个好主意,因为它们通常包含您不希望或不需要用户看到的东西(包括循环引用)。我认为最好的解决方案是拥有另一组可以映射到的模型(DTO 或“响应模型”)。
【解决方案2】:

Nate 的回答非常完美,他给出了很好的建议,指出我要为我的场景找到很好的解决方案。我决定分享它以防有人遇到类似问题。提醒一下,这是带有 Api 的 ASP.NET Core Model First MVC 项目。我使用 AutoMapper 将我的 Model First 类映射到相应的视图模型。

这是我的 Model First 课程:

public class Category
{
    public Category()
    {
        Tasks = new HashSet<Task>();
    }

    public int CategoryId { get; set; }
    public string Name { get; set; }
    public DateTime Timestamp { get; set; }
    public string Username { get; set; }

    public ApplicationUser ApplicationUser { get; set; }

    public virtual ICollection<Task> Tasks { get; set; }
}

public class Task
{
    public int TaskId { get; set; }
    public string Name { get; set; }
    public DateTime Timestamp { get; set; }
    public int CategoryId { get; set; }

    public virtual Category Category { get; set; }
}

对应的视图模型:

public class CategoryViewModel
{
    public int CategoryId { get; set; }
    public string Name { get; set; }
    public DateTime Timestamp { get; set; }

    public IEnumerable<TaskViewModel> Tasks { get; set; }
}

public class TaskViewModel
{
    public int TaskId { get; set; }
    public string Name { get; set; }
    public DateTime Timestamp { get; set; }
}

“Setup.cs”文件中的 AutoMapper 设置:

Mapper.Initialize(config =>
        {
            config.CreateMap<Category, CategoryViewModel>().ReverseMap();
            config.CreateMap<Task, TaskViewModel>().ReverseMap();
        });

最后是我完整的 Api 方法:

[HttpGet]
    public JsonResult Get()
    {
        var result = Mapper.Map<IEnumerable<CategoryViewModel>>(_repo.GetAllForUser("lucas@test.com"));

        return Json(result);
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-14
    相关资源
    最近更新 更多