TLDR :我想知道是否可以在EF核心的单个查询中为一种实体类型使用不同的“包含逻辑”。
编辑:为清楚起见,我在标题中说的是跟踪实体,因为我认为这就是EF所做的事情,但是BitConverter
在此之前没有任何用处。 / p>
当前的问题是在相对较小的React应用程序上,该应用程序以ASP.NET Core Web API应用程序为后盾。我要完成的是,当我调用.AsNoTracking()
时,我希望应用程序给我一个像这样的json:
api/parents
我的设置如下:
EF查询:
[
{
"id": "parentid1",
"extraProperty": "value",
"children": [
{
"id": "childid1",
"parent": {
"id": "parentid1",
"extraProperty": "value"
}
}
]
}
]
之后,我将实体映射到dto的AutoMapper进行了很多操作。我还为该应用程序使用了默认的Json序列化器(Newtonsoft)。它使用(from p in _context.Parents
select p)
.Include(p => p.Children)
.ThenInclude(c => c.Parent)
.ToList();
配置。
通过此设置,api调用会返回以下响应:
SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore
您会看到“忽略”了父级的自我引用。
我想出的解决方案是,我应该配置Newtonsoft以“序列化”参考循环,然后我尝试了一下。并抛出该错误,因为上面提供的EF查询返回的实体列表如下所示:
[
{
"id": "parentid1",
"extraProperty": "value",
"children": [
{
"id": "childid1"
}
]
}
]
如果查看我的Include调用,它会明确表示对于我的子对象,我只需要父对象,而在该父对象中没有其他引用。
据我所知,EF在遇到该查询的任何父对象时会使用初始设置[
{
"id": "parentid1",
"extraProperty": "value",
"children": [
{
"id": "childid1",
"parent": {
"id": "parentid1",
"extraProperty": "value",
"children": [
{
"id": "childid1",
"parent": {
"id": "parentid1",
"extraProperty": "value"
...
}
}
]
}
}
]
}
]
。因此,当它在.Include(child).ThenInclude(parent)
对象中找到一个Parent
对象时,它使用Child
而不是不使用包含(在ThenInclude
之后没有任何包含)。 / p>
如果不需要,我不想通过使用映射器或序列化器进行破解来解决此问题。我想知道我所寻找的是否可能:在单个查询中为一个实体类型使用不同的“包含逻辑”。
答案 0 :(得分:1)
ThenInclude(parent)
调用是多余的,因为您是从父级开始的-物化的子级将已经具有其Parent
属性。您需要按照第一条评论中的@IvanStoev定制序列化。
另一种方法是按父级查询子级,然后投影所需的结果:
var parents = _context.Children
.Include( c => c.Parent )
.GroupBy( c => c.Parent )
.ToArray()
.Select( g =>
{
// assign children to g.Key (the parent object)
g.Key.Children = g.ToArray(); // I don't know type of `Children` property
// select parent
return g.Key;
} );
答案 1 :(得分:0)
我决定从最前端解决问题,因为其他解决方案对我不起作用。为了将来参考,我将发布我的旅程:
EF
起初,在数据访问层上解决问题似乎是合乎逻辑的,因为我只是错过了一个非常基本的东西。我意识到在评论后采取这种方式是不合逻辑的。基本上,当您从EF获取实体并且您的对象具有引用时,每次您拥有一个以上对象时,它们都是同一对象(通过引用而不是值)。因此,期望它们中包含不同的数据(在我的情况下,其中一个具有一些细节而另一个则没有)是不合逻辑的。
那之后,我想也许我可以在制图阶段解决这个问题。我尝试针对不同的场景进行不同的配置文件/配置,但它变得非常难看。然后,我认为也许使用单个Profile并执行一些AfterMap()
逻辑就可以了(首先加载所有数据,然后剥离掉不需要的数据)。但是,除非您做一些丑陋的事情,否则适用相同的原理。 AutoMapper还会保留引用,因此当您修改child.Parent
对象时,原始的Parent也将被修改。我本可以使用克隆并做一些其他的技巧,但就像我说的那样,这在我看来很丑。
目前,我正在React应用程序上手动执行任务。当我从服务器获取数据时,我只是做
parent.children.forEach(c => c.parent = parent);
请牢记parent.children.parent
是null
。实体对象或DTO并非如此,序列化之后就是这种情况。因为我为串行器设置了ReferenceLoopHandling.Ignore
。
这解决了我的应用程序所有问题,但我的api似乎不完整。返回的JSON似乎丢失了某些内容(也许就是我,IDK)。
总而言之,从长远来看,我考虑在不同情况下使用多个具有相同对象类型的DTO,或者出于完整性考虑,在api中返回id字段。
感谢所有评论和答复。