如何优化Razor Pages脚手架生成的EF Core关联查询?
解决EF关联查询加载冗余字段的最小改动方案
嘿,这个问题我之前也遇到过——脚手架生成的代码确实会默认加载关联实体的所有字段,不过我们只需要微调查询逻辑就能搞定,而且改动非常小,完全不用动实体类或者其他核心代码。
1. 用投影查询精准选择字段(最推荐,改动最少)
首先找到你在Todo详情页/编辑页里获取数据的代码(一般在Controller或者PageModel的Details/Edit方法里),原来的代码大概是这样的:
var todo = await _context.Todos .Include(t => t.Category) .FirstOrDefaultAsync(t => t.Id == id);
只需要把这段代码改成投影查询,明确指定要加载的Category字段:
// 用匿名类型临时存储需要的数据,不用额外定义类 var todoData = await _context.Todos .Where(t => t.Id == id) .Select(t => new { // 保留Todo的所有字段 t.Id, t.Description, t.DueDate, t.CategoryId, // 只加载Category需要的字段 CategoryName = t.Category.Name }) .FirstOrDefaultAsync();
之后在视图里,直接用todoData.CategoryId和todoData.CategoryName就可以了,完全不需要用到DummyData。如果想要强类型(避免视图里用匿名类型的麻烦),可以加个超简单的DTO类(这也算极小的改动):
// 新建一个只存必要数据的DTO类 public class TodoViewDto { public int Id { get; set; } public string Description { get; set; } public DateTime? DueDate { get; set; } public int CategoryId { get; set; } public string CategoryName { get; set; } }
然后查询改成返回这个DTO:
var todoDto = await _context.Todos .Where(t => t.Id == id) .Select(t => new TodoViewDto { Id = t.Id, Description = t.Description, DueDate = t.DueDate, CategoryId = t.CategoryId, CategoryName = t.Category.Name }) .FirstOrDefaultAsync();
这样生成的SQL就会自动去掉DummyData,只查询你需要的字段:
SELECT [t].[Id], [t].[Description], [t].[DueDate], [t].[CategoryId], [c].[Name] FROM [Tasks] AS [t] INNER JOIN [Categories] AS [c] ON [t].[CategoryId] = [c].[Id] WHERE [t].[Id] = @id
2. EF Core专属:用Include+ThenSelect限制加载字段(适合不想改返回类型的场景)
如果你用的是EF Core 5及以上版本,还可以用Include配合ThenSelect来筛选关联实体的字段,但注意这种方式需要返回一个包含部分属性的Category对象(不能直接返回原始的Todo实体,因为实体的导航属性要求完整对象):
var todo = await _context.Todos .Where(t => t.Id == id) .Select(t => new { Todo = t, Category = new { t.Category.Id, t.Category.Name } }) .FirstOrDefaultAsync();
这种方式同样能生成只包含必要字段的SQL,而且不用新建太多额外代码。
为啥不直接删实体里的DummyData?
看你说应用运行正常,说明这个字段大概率在其他地方有用,所以绝对不能直接删实体属性——投影查询的好处就是完全不影响其他功能,只在需要的页面限制加载字段。
内容的提问来源于stack exchange,提问作者Sudhashbahu
相关产品推荐
相关产品推荐

