我正在开发一个应用程序,试图提高性能。显然,我会进行自己的分析和测试,但我想知道是否有“共识”或已知的最佳实践。
在旧的SQL时代,提高效率的主要方法之一是不选择您不需要使用的数据。我正试图通过EF6走这条路线。
在这种特定情况下,我有一个主-详细-详细关系,在屏幕上需要呈现有关父项、子项和孙项的一些数据。
我的应用程序是n层结构,具有MVC前端和Web-API REST后端。这些实体最终将被序列化为JSON,并通过REST连接发送回MVC控制器,在那里它们将呈现到屏幕上。在这种情况下,我不需要担心从此流程更新实体(在这些情况下,为了方便维护,我可能会发送完整实体)。
因此,我编写的原始EF代码如下:
然而,我实际上只使用这些实体的属性子集,其中一些未使用的属性可能相当大(大块的XML等)。
这是尝试仅投射我需要的字段的第一步(对于此示例,我已经剪切并重命名了大多数我实际选择以提高可读性的字段,但通常我使用全实体的5-20%)。
这显然使用了匿名类型(我认为在EF中这是必需的,没有一种方法可以将其投影到命名类型中?)(编辑:显然您可以将其投影到未映射的命名类型中,但在这种情况下,查询的返回类型是已映射的类型。因此,我可以创建DTO,但那需要更多的代码来维护)。所以我必须回到我的具体类型。我当然可以生成仅具有所需属性的DTO,但我不认为这会改变所使用的基本逻辑,也可能不会改变性能特征。我尝试了Automapper和ValueInjecter,但似乎都不完全符合要求(深克隆异构类型并匹配名称),所以我采用了一种不太正规的方法。
这有点麻烦,因为它只是作为REST调用的一部分而再次序列化。我可能可以覆盖webAPI调用,以表明我正在返回已经序列化的数据,这将让我跳过实体类型的再次处理(因为所有属性名称都匹配,REST客户端应该能够像上面的片段一样重新处理匿名类型)
在旧的SQL时代,提高效率的主要方法之一是不选择您不需要使用的数据。我正试图通过EF6走这条路线。
在这种特定情况下,我有一个主-详细-详细关系,在屏幕上需要呈现有关父项、子项和孙项的一些数据。
我的应用程序是n层结构,具有MVC前端和Web-API REST后端。这些实体最终将被序列化为JSON,并通过REST连接发送回MVC控制器,在那里它们将呈现到屏幕上。在这种情况下,我不需要担心从此流程更新实体(在这些情况下,为了方便维护,我可能会发送完整实体)。
因此,我编写的原始EF代码如下:
Repository.GetAll()
.AsNoTracking()
.Include("Children")
.Include("Children.GrandChildren")
.ToList();
然而,我实际上只使用这些实体的属性子集,其中一些未使用的属性可能相当大(大块的XML等)。
这是尝试仅投射我需要的字段的第一步(对于此示例,我已经剪切并重命名了大多数我实际选择以提高可读性的字段,但通常我使用全实体的5-20%)。
var projection = Repository.GetAll()
.AsNoTracking()
.Select(r => new
{
r.Id,
r.RandomId,
r.State,
r.RequestType,
r.CreatedDate,
r.CreatedBy,
Children = r.Children.Select(r2 => new
{
r2.Id,
r2.Status,
GrandChildren = r2.GrandChildren.Select(r3 => new
{
r3.Id,
r3.Status,
r3.GrandChildType
})
}),
}
).ToList();
这显然使用了匿名类型(我认为在EF中这是必需的,没有一种方法可以将其投影到命名类型中?)(编辑:显然您可以将其投影到未映射的命名类型中,但在这种情况下,查询的返回类型是已映射的类型。因此,我可以创建DTO,但那需要更多的代码来维护)。所以我必须回到我的具体类型。我当然可以生成仅具有所需属性的DTO,但我不认为这会改变所使用的基本逻辑,也可能不会改变性能特征。我尝试了Automapper和ValueInjecter,但似乎都不完全符合要求(深克隆异构类型并匹配名称),所以我采用了一种不太正规的方法。
var json = projection.Select(JsonConvert.SerializeObject).ToList();
var mapped = json.Select(JsonConvert.DeserializeObject<Parent>).ToList();
这有点麻烦,因为它只是作为REST调用的一部分而再次序列化。我可能可以覆盖webAPI调用,以表明我正在返回已经序列化的数据,这将让我跳过实体类型的再次处理(因为所有属性名称都匹配,REST客户端应该能够像上面的片段一样重新处理匿名类型)
但是,所有这些似乎是很多工作,代码也更不易维护,可能会出现更多的错误等,特别是在Entity Framework似乎不支持的情况下。但我那旧学派的本能无法放弃这个想法:我选择、序列化和传输了大量数据,但最终却没有使用。
这在内部是否产生合理的SQL?这是否值得双重序列化?(假设我没有找出如何覆盖webapi,让它接收我的数据)
我想我的另一个选择就是重构所有实体,使未使用的属性位于不同的子实体中,以便我可以忽略它,但这将需要对整个系统进行大量的重构(相比于能够在关键点上进行精确的性能优化),并且这似乎也不是一种好的选择,在设计实体时应该遵循标准的归一化规则等,而不是根据我所使用的ORM。
Id = r.Id,
等等。当不初始化匿名类型时,必须指定属性名称。 - CodeCaster