C# EFcore LazyLoading—在子属性上进行筛选时,向数据库请求什么

C# EFcore LazyLoading—在子属性上进行筛选时,向数据库请求什么,c#,ef-core-2.2,C#,Ef Core 2.2,我使用的是延迟加载,我关心的是性能 假设我有一个对象A,它包含一个对象B的列表 我将使用延迟加载从数据库中检索对象A。这意味着如果我不访问对象B的列表,它将不会被检索 但如果我这样做: ObjectA.ObjectsB.Where(b => b.Id == 12); 它将查询所有对象B并对其进行过滤,还是只查询表达式的结果?如果您使用的是SQL Server,则可以使用SQL Server探查器查看SQL正在执行的查询类型 我提出了以下问题: var context =

我使用的是延迟加载,我关心的是性能

假设我有一个对象A,它包含一个对象B的列表

我将使用延迟加载从数据库中检索对象A。这意味着如果我不访问对象B的列表,它将不会被检索

但如果我这样做:

ObjectA.ObjectsB.Where(b => b.Id == 12);

它将查询所有对象B并对其进行过滤,还是只查询表达式的结果?

如果您使用的是SQL Server,则可以使用SQL Server探查器查看SQL正在执行的查询类型

我提出了以下问题:

        var context = new LabContextDb();
        var user = context.Users.FirstOrDefault(x => x.Id == 1);
        var roles = user.UserRoles.Where(x => x.UserId == 1).ToList();
我的SQL是:

SELECT TOP(1) [x].[Id] FROM [AbpUsers] AS [x] WHERE [x].[Id] = CAST(1 AS bigint)

exec sp_executesql N'SELECT [e].[Id], [e].[RoleId], [e].[UserId]
FROM [AbpUserRoles] AS [e]
WHERE [e].[UserId] = @__get_Item_0',N'@__get_Item_0 bigint',@__get_Item_0=1
但如果我这样做:

var user = context.Users.FirstOrDefault(x => x.UserRoles.Any(y => y.Id == 1));
  SELECT TOP(1) [x].[Id]
  FROM [AbpUsers] AS [x]
  WHERE EXISTS (SELECT 1
  FROM [AbpUserRoles] AS [y]
  WHERE ([y].[Id] = CAST(1 AS bigint)) AND ([x].[Id] = [y].[UserId]))
我的SQL将如下所示:

var user = context.Users.FirstOrDefault(x => x.UserRoles.Any(y => y.Id == 1));
  SELECT TOP(1) [x].[Id]
  FROM [AbpUsers] AS [x]
  WHERE EXISTS (SELECT 1
  FROM [AbpUserRoles] AS [y]
  WHERE ([y].[Id] = CAST(1 AS bigint)) AND ([x].[Id] = [y].[UserId]))

SQLServerProfiler是查看幕后发生的事情的一种好方法。另一个技巧是使用.ToList(),如果您需要在大列表中进行迭代,则不会在每次循环发生时执行您的语句,因为当您使用Where conditions返回IQueryable(…)。

好的,因此在阅读Flavio Francisco的答案后,我自己使用SQL Profiler进行了测试

var objectA = (await _repo.Object.FindByCondition(obj => obj.Id == 3)).FirstOrDefault();
var objectB = objectA.ObjectsB.Where(obj => obj.Id == 1);
在探查器中,它首先调用对象a,然后请求所有对象B


因此,遗憾的是,过滤器是在代码级别进行的。

也许它适合您

var objectB = (from objA in _repo.Object join objB in ObjectsB 
               on objA.Id equals objB.ObjectAId
               where objA.Id == 3 && objB.Id == 1
               select u).ToList();

为了避免调用所有对象B。

实际上,您可以按照与ef core文档相同的文档中的说明进行操作:

dbContext.Entry(ObjectA)
    .Collection(b => b.ObjectsB)
    .Query()
    .Where(b => b.Id == 12)
    .Load();

然后使用Sql Profiler跟踪它,并查看它是否在数据库级别被过滤。

如果您担心性能,请完全关闭延迟加载。是的,它将首先将所有内容放入内存,然后进行过滤,这是资源的浪费OP希望有条件地检索相关数据,而您的第二个代码仍在检索第一个对象,但条件过多。