我的网站有很多帖子,每个帖子的详细信息页面都会显示相关信息以及该帖子所标记的标签[多对多数据]。
代码:
public async Task<IActionResult> Detail(int id)
{
var Post = await _postInterface.GetByIdAsync(id);
var Tag = await _context.Posts.Include(p => p.PostTags)
.ThenInclude(t => t.Tag).ToListAsync();
return View(Post);
}
var Post
根据所选的 id
显示数据库数据。var Tag
从多对多关系数据库表初始化。我的问题:
有没有办法只初始化内容而不使用这里的
var Tag =
,因为它不是必需的?
await _context.Posts.Include(p => p.PostTags).ThenInclude(t => t.Tag).ToListAsync();
为了解释您所看到的情况,请对代码进行分解:
var Post = await _postInterface.GetByIdAsync(id);
这一行从 _postInterface 类中获取单个帖子。这通常称为存储库模式,并且通常有一个缺点,即它不能轻松地促进诸如急切加载调用者可能想要的相关数据之类的事情。
var Tag = await _context.Posts.Include(p => p.PostTags)
.ThenInclude(t => t.Tag).ToListAsync();
这一行实际上并不是在加载一个或多个标签,它实际上是在加载ALL帖子,并将所有包含的标签加载到“Tag”变量和DbContext的跟踪缓存中。 (非常糟糕!!!)
这似乎有效的原因是因为在调用“读取所有内容”之后,DbContext 现在正在跟踪所有帖子和标签,因此当您返回加载的“帖子”时,对其 PostTags/Tag 的代理引用可以是当使用代码尝试访问它们时,由 DbContext 填充。如果删除该
var Tags = ....
,则 Context 不会跟踪它们,并且您会得到 #null,或者如果启用了延迟加载,只要 DbContext 未释放,它就会在访问时获取它们。
你真正想要的是:
public async Task<IActionResult> Detail(int id)
{
var post = await _context.Posts
.Include(p => p.PostTags)
.ThenInclude(t => t.Tag)
.AsNoTracking()
.SingleAsync(p => p.Id == id);
return View(post);
}
这告诉 EF 我们想要一篇文章,但要包含该文章的 postTags 和 Tags。
AsNoTracking()
调用是可选的,但在这种情况下使其速度更快一点,因为它告诉 EF DbContext 不需要跟踪这些实体。如果您返回分离的实体,则 Context 不需要跟踪实体,因为 DbContext 无论如何都应该在请求结束时被处置。例如,如果您碰巧将 .AsNoTracking()
添加到原始 var Tags = ....
查询中,您实际上会发现您的代码停止工作,因为当您返回“时,您的代码依赖于跟踪缓存行为来填充引用”帖子”。
您必须修改
GetByIdAsync
方法才能获取带有相关标签的选定帖子。
public async Task<Post> GetByIdAsync(int id)
{
return await _context.Posts
.Include(p => p.PostTags)
.ThenInclude(t => t.Tag)
.FirstOrDefaultAsync(p => p.Id == id);
}
如果您想保留
GetByIdAsync
的当前实现,建议创建另一个方法,例如:具有上述实现的 GetPostWithTagsAsync
。不要忘记在您的 PostInterface
中添加方法(签名,不带方法主体)。
public async Task<Post> GetPostWithTagsAsync(int id)
{
return await _context.Posts
.Include(p => p.PostTags)
.ThenInclude(t => t.Tag)
.FirstOrDefaultAsync(p => p.Id == id);
}
public interface IPostInterface
{
Task<Post> GetPostWithTagsAsync(int id);
}