【问题标题】:How to load entity in EF Core with the 'AsNoTracking' method in combination with explicitly loading of related entities如何使用“AsNoTracking”方法在 EF Core 中加载实体并结合显式加载相关实体
【发布时间】:2020-04-20 07:09:18
【问题描述】:

我目前正在使用这种方法通过AsNoTracking 加载实体及其相关实体:

await DbContext.Clients
                .Include(x => x.AllowedGrantTypes)
                .Include(x => x.RedirectUris)
                .Include(x => x.PostLogoutRedirectUris)
                .Include(x => x.AllowedScopes)
                .Include(x => x.ClientSecrets)
                .Include(x => x.Claims)
                .Include(x => x.IdentityProviderRestrictions)
                .Include(x => x.AllowedCorsOrigins)
                .Include(x => x.Properties)
                .Where(x => x.Id == clientId)
                .AsNoTracking()
                .SingleOrDefaultAsync();

Github 上的代码详情:link

这可行,但在迁移到 EF Core 3.0 后,此查询非常慢。

我发现可以通过像这样显式加载相关实体来解决这个性能问题:

IQueryable<Entities.Client> baseQuery = Context.Clients
                .Where(x => x.Id == clientId)
                .Take(1);

            var client = await baseQuery.FirstOrDefaultAsync();
            if (client == null) return null;

            await baseQuery.Include(x => x.AllowedCorsOrigins).SelectMany(c => c.AllowedCorsOrigins).LoadAsync();
            await baseQuery.Include(x => x.AllowedGrantTypes).SelectMany(c => c.AllowedGrantTypes).LoadAsync();
            await baseQuery.Include(x => x.AllowedScopes).SelectMany(c => c.AllowedScopes).LoadAsync();
            await baseQuery.Include(x => x.Claims).SelectMany(c => c.Claims).LoadAsync();
            await baseQuery.Include(x => x.ClientSecrets).SelectMany(c => c.ClientSecrets).LoadAsync();
            await baseQuery.Include(x => x.IdentityProviderRestrictions).SelectMany(c => c.IdentityProviderRestrictions).LoadAsync();
            await baseQuery.Include(x => x.PostLogoutRedirectUris).SelectMany(c => c.PostLogoutRedirectUris).LoadAsync();
            await baseQuery.Include(x => x.Properties).SelectMany(c => c.Properties).LoadAsync();
            await baseQuery.Include(x => x.RedirectUris).SelectMany(c => c.RedirectUris).LoadAsync();

Github 上的代码详情:link

不幸的是,我尝试使用 AsNoTracking 方法重写此示例,但它不起作用 - 未加载相关实体。

如何使用 AsNoTracking 方法通过更快的性能重写我的原始查询?

我不需要为我的用例跟踪客户端实体。

【问题讨论】:

    标签: asp.net-core .net-core ef-core-3.0


    【解决方案1】:

    正如 EF Core 文档所说,v3 现在生成连接,并且此查询是笛卡尔爆炸问题的受害者https://docs.microsoft.com/en-us/ef/core/querying/related-data

    文档还说,在以前的版本中,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;

    【讨论】:

      【解决方案2】:

      简单的答案是你不能而且你不应该。 首先,您在原始通话中所做的事情看起来很危险,目的是什么? 无论如何,包含速度很慢并且需要连接。但我记得 AsNoTracking 总是应该在实体调用之后直接进行。然后包括例如。 Where 情况也是如此,直接将其放在 AsNoTracking 之后,看看是否有区别。

      但请仔细阅读此内容Related Data Docs, EF Core 特别是关于显式加载的部分:

      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();
      }
      

      以及相关实体这允许您执行诸如在相关实体上运行聚合运算符等操作,而无需将它们加载到内存中。

      using (var context = new BloggingContext())
      {
          var blog = context.Blogs
              .Single(b => b.BlogId == 1);
      
          var postCount = context.Entry(blog)
              .Collection(b => b.Posts)
              .Query()
              .Count();
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-08-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-29
        • 2023-02-04
        相关资源
        最近更新 更多