Asp.net core 如何使用';AsNoTracking';方法与显式加载相关实体相结合
我目前正在使用此方法加载实体及其相关实体,并使用Asp.net core 如何使用';AsNoTracking';方法与显式加载相关实体相结合,asp.net-core,.net-core,ef-core-3.0,Asp.net Core,.net Core,Ef Core 3.0,我目前正在使用此方法加载实体及其相关实体,并使用AsNoTracking: wait DbContext.Clients .Include(x=>x.AllowedGrantTypes) .Include(x=>x.RedirectUris) .Include(x=>x.postlogoutredirecuris) .包括(x=>x.AllowedScope) .Include(x=>x.ClientSecrets) .包括(x=>x.索赔) .Include(x=>x.IdentityProv
AsNoTracking
:
wait DbContext.Clients
.Include(x=>x.AllowedGrantTypes)
.Include(x=>x.RedirectUris)
.Include(x=>x.postlogoutredirecuris)
.包括(x=>x.AllowedScope)
.Include(x=>x.ClientSecrets)
.包括(x=>x.索赔)
.Include(x=>x.IdentityProviderRestrictions)
.包括(x=>x.允许的公司名称)
.Include(x=>x.Properties)
.Where(x=>x.Id==clientId)
.AsNoTracking()
.SingleOrDefaultAsync();
Github上的代码详细信息:
这是可行的,但是在迁移到EF Core 3.0之后,这个查询非常慢
我发现,可以通过加载相关实体来明确解决此性能问题,如下所示:
IQueryable baseQuery=Context.Clients
.Where(x=>x.Id==clientId)
.采取(1)项措施;
var client=await baseQuery.FirstOrDefaultAsync();
if(client==null)返回null;
等待baseQuery.Include(x=>x.AllowedCorsOrigins)。选择Many(c=>c.AllowedCorsOrigins)。LoadAsync();
等待baseQuery.Include(x=>x.AllowedGrantTypes)。选择Many(c=>c.AllowedGrantTypes)。LoadAsync();
等待baseQuery.Include(x=>x.AllowedScopes);
等待baseQuery.Include(x=>x.Claims)。选择many(c=>c.Claims)。LoadAsync();
等待baseQuery.Include(x=>x.ClientSecrets)。选择many(c=>c.ClientSecrets)。LoadAsync();
等待baseQuery.Include(x=>x.IdentityProviderRestrictions)。选择Many(c=>c.IdentityProviderRestrictions)。LoadAsync();
等待baseQuery.Include(x=>x.PostLogoutRedecturis)。选择many(c=>c.PostLogoutRedecturis)。LoadAsync();
等待baseQuery.Include(x=>x.Properties)。选择many(c=>c.Properties)。LoadAsync();
等待baseQuery.Include(x=>x.RedirectUris)。选择many(c=>c.RedirectUris)。LoadAsync();
Github上的代码详细信息:
不幸的是,我曾尝试使用AsNoTracking
方法重写此示例,但它不起作用-未加载相关实体
如何使用AsNoTracking方法通过更快的性能重写原始查询
我不需要为我的用例跟踪客户实体。简单的答案是你不能也不应该。 首先,你在最初的通话中所做的看起来很危险,目的是什么? 尽管如此,include的速度很慢,需要连接。但我记得AsNoTracking总是应该在实体调用之后直接进行。然后,以包含为例。Where的情况也一样,直接放在AsNoTracking之后,看看它是否有区别 但请仔细阅读 特别是关于显式加载的部分:
using (var context = new BloggingContext())
{
var blog = context.Blogs
.Single(b => b.BlogId == 1);
context.Entry(blog)
.Collection(b => b.Posts)
.Load();
context.Entry(blog)
.Reference(b => b.Owner)
.Load();
}
和相关实体这允许您在相关实体上运行聚合操作符,而无需将它们加载到内存中
正如EF核心文档所述,v3现在生成联接,而此查询是笛卡尔爆炸问题的受害者 文档还指出,在以前的版本中,EF为每个包含生成单独的查询。所以在我看来,好的解决办法是
var baseEntity = await DbContext.Clients.AsNoTracking().SingleOrDefaultAsync(x => x.Id == clientId);
baseEntity.AllowedGrantTypes = await DbContext.ClientCorsOrigins.AsNoTracking().Where(x => x.ClientId == clientID).ToListAsync();
baseEntity.RedirectUris = await DbContext.ClientRedirectUris.AsNoTracking().Where(x => x.ClientId == clientID).ToListAsync();
...
...
依此类推,直到您获得所需的所有相关资源。它将模仿以前的行为。
如果您确实需要使用导航属性,那么很遗憾,您不能使用.AsNoTracking()
。如果我的想法看起来太幼稚,那么我唯一能想到的另一种方法就是在使用导航属性之后将实体从跟踪上下文中分离出来
所以,在从第二个链接实现代码之后,您需要遍历对象中的实体,并将它们标记为已分离
DbContext.Entry(entity.State=EntityState.Detached代码>
var baseEntity = await DbContext.Clients.AsNoTracking().SingleOrDefaultAsync(x => x.Id == clientId);
baseEntity.AllowedGrantTypes = await DbContext.ClientCorsOrigins.AsNoTracking().Where(x => x.ClientId == clientID).ToListAsync();
baseEntity.RedirectUris = await DbContext.ClientRedirectUris.AsNoTracking().Where(x => x.ClientId == clientID).ToListAsync();
...
...