集合导航属性返回奇数 JSON 层次结构



我有 2 个类:

public class A
{
public int Id { get; set; }
public string Name { get; set; }
public B myB { get; set; }
}
public class B
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<A> myAs { get; set; }
}

我使用邮递员来测试 API 调用。

public IEnumerable<B> GetBs()
{
return _context.Bs.Include(b => b.myAs).ToList();
}

按预期返回 B 对象及其关联的 A 对象的列表:

{
"Id": 1,
"Name": "B2",
"myAs": [
{
"Id": 1,
"Name": "A1"
},
{
"Id": 2,
"Name": "A2"
}
]
},
{
"Id": 2,
"Name": "B3",
"myAs": [
{
"Id": 3,
"Name": "A3"
},
{
"Id": 4,
"Name": "A4"
},
{
"Id": 5,
"Name": "A5"
}
]
}

然而,相反的情况返回一个奇怪的层次结构:

public IEnumerable<A> GetAs()
{
return _context.As.Include(a => a.myB).ToList();
}

返回:

[
{
"Id": 1,
"Name": "A1",
"myB": {
"Id": 1,
"Name": "B2",
"myAs": [
{
"Id": 2,
"Name": "A2"
}
]
}
},
{
"Id": 2,
"Name": "A2",
"myB": {
"Id": 1,
"Name": "B2",
"myAs": [
{
"Id": 1,
"Name": "A1"
}
]
}
},
{
"Id": 3,
"Name": "A3",
"myB": {
"Id": 2,
"Name": "B3",
"myAs": [
{
"Id": 4,
"Name": "A4"
},
{
"Id": 5,
"Name": "A5"
}
]
}
}
]

GetAs 方法返回 A 对象,其中包含具有进一步嵌套的 A 对象的 B 对象。

经过一番研究(我在这里可能大错特错(,我的理解是,因为 A 具有 B (myB( 的导航属性,而 B 具有指向 A 对象列表 (myAs( 的导航属性,这会导致某种循环。

我的问题是

  1. 我在这里的理解正确吗?这就是层次结构以这种奇怪的布局返回的原因吗?
  2. 我该如何解决这个问题?我可以将 ICollection 导航属性从域模型中取出,但随后我无法再查询 As 及其关联的 B?

注意 A 和 B 实际上不是我的域模型。我只是想让示例尽可能简单。

提前谢谢。

这里有几件事:

输出具有预期的形状。正如您所怀疑的那样,序列化程序正在扩展双向引用。想想如果以递归方式手动序列化每个对象的每个属性会发生什么。这就是正在发生的事情。

若要解决当前问题,请按如下所示配置默认序列化程序设置:

jsonFormatter.SerializerSettings.ReferenceLoopHandling  = Newtonsoft.Json.Serialization.ReferenceLoopHandling.Ignore;

上述内容在原型设计时很有用,但当应用程序更加正式时,应从 Web API 终结点创建并返回专用视图模型类型。

public class AViewModel
{
public int Id { get; set; }
public string Name { get; set; }
}
public class BViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public IEnumerable<AViewModel> myAs { get; set; }
}
public IEnumerable<BViewModel> GetBs()
{
return _context.Bs.Include(b => b.myAs)
.Select(b => new BViewModel
{
Id = b.Id,
Name = b.Name,
As = b.As.Select(a => new AViewModel
{
Id = a.Id,
Name = a.Name
})
})
.ToList();
}

值得注意的是,有一些库,例如备受推崇的AutoMapper,可以为您执行模型类型之间的这些转换,使用反射按名称自动分配相应的属性。

就个人而言,我尽量避免基于反射的方法,因为它们往往会使代码难以静态推理。这既阻碍了像我们这样的人类读者,也阻碍了像C#语言这样的工具。

也就是说,根据手头的任务,权衡是值得的。我希望最终看到语言级别的支持,在不进入严格领域的情况下消除此类样板作业,但我还有很长的等待时间。

相关内容

  • 没有找到相关文章

最新更新