我有两个班:
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; }
}
我使用Postman来测试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方法返回带有B对象的A对象和进一步嵌套的A对象。
经过一点研究后,我的理解是(我可能是非常错误的),因为A有一个导航属性到B(myB),B有一个导航属性到A对象列表(myAs),这会导致某种循环。
我的问题是
1.我的理解是否正确?这就是为什么层次结构在这种奇怪的布局中回归的原因吗?
1.我该怎么解决这个问题?我可以将ICollection导航属性从域模型中取出,但这样我就不能再查询A及其关联的B了?
注意A和B实际上不是我的域模型。我只是想让这个例子尽可能简单。
先谢谢你了。
1条答案
按热度按时间j13ufse21#
这里有几件事:
输出具有预期的形状。正如您所怀疑的,双向引用正在被序列化程序展开。想想如果手动递归序列化每个对象的每个属性会发生什么。这就是正在发生的事情。
要解决眼前的问题,请像这样配置默认的序列化程序设置:
上述内容在原型设计时很有用,但当应用程序更加正式时,您应该从Web API端点创建并返回专用的视图模型类型。
值得注意的是,有一些库,比如广受好评的AutoMapper,可以在模型类型之间执行这些转换,使用反射按名称自动分配相应的属性。
就我个人而言,我尽量避免基于反射的方法,因为它们往往会使代码难以静态推理。这既妨碍了像我们这样的人类读者,也妨碍了像C#语言这样的工具。
也就是说,根据手头的任务,这可能是值得的。我希望最终能看到语言级别的支持,消除这种样板任务,而不会进入字符串的领域,但我还有很长的时间要等待。