У юзера есть организация, у организации есть юзеры. Юзаем такой код:
_dbContext.Organizations.Where(o => o.Name.Contains("Roga") && o.Users.Any(u => u.FirstName == "Petr")).Select(...)
в организациях ищем только те организации, у которых имя содержит "Roga" и в юзерах есть хотя бы 1 Петр.
Тут у нас две лямбды: внешняя, которая Expression, потому что передается в Where, который вызывается у DbSet, который реализует IQueryable. И внутренняя, которая Func , потому что вызывается у ICollection, она не Expression, потому что ICollection не реализует IQueryable.
Т.к. первая лямбда - Expression, то query provider (или что там парсит запрос) сможет его распарсить и получить инфу обо всех "узлах", которые заюзаны в выражении: о полях объекта(o.Name, o.Users) , о переменных ("Roga"), об операторах (в данном случае &&) и о методах, которые вызываются на полях(методы Any и Contains, и дальше парсер, зная, что вызываются Any и Contains, уже поймет, в какие sql-команды преобразовать эти методы). Это как я понимаю происходящее при использовании Expression.
Но тут меня напрягает то, что мы в метод Any передаем вложенную лямбду, которая не является экспрешшеном. Ведь парсер должен получить изнутри вложенной лямбды инфу о том, что "у Юзера нам нужно взять поле FirstName и сравнить его с "Petr"). Но вложенная лямбда не является экспрешшеном, т.е., распарсить ее не получится? Или несмотря на то, что вложенная лямбда - не Expression, а Func, но за счет того, что она внутри экспрешшена, сишарп все-равно ее передаст как Expression и она сможет быть распарсена?
Я провел тест, который показал, что запрос в БД содержал фильтрацию по юзернейму "Petr", вот такой был запрос:
FROM [dbo].[Organizations] AS [Extent1]
WHERE ([Extent1].[Name] LIKE N'%Roga%') AND ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[AspNetUsers] AS [Extent3]
WHERE ([Extent1].[Id] = [Extent3].[OrganizationId]) AND (N'Petr' = [Extent3].[FirstName])
))
что говорит о том, что лямбда-Func была распарсена движком подобно лямбде-экспрешшену. За счет чего это произошло?
Я, вообще, раньше считал, что тут вложенная лямбда распарсится (каким бы то ни было образом), что, собсвенно, и произошло. Но вот я только что захотел вынести вложенную функцию в отдельную переменную, чтобы заюзать ее дальше в Select. Т.е., я хотел сделать так:
Func<User, bool> func = u => u.FirstName == "Petr";
return _dbContext.Organizations.Where(o => o.Name.Contains("Roga") && o.Users.Any(func)).Select(...)
и тут я подумал, если я так сделаю, то это уже будет конкретный вызов сишарповой переменной func изнутри экспрешшена, а это явно должно привести к падению. Я проверил этот код, и он реально кинул эксепшен
Internal .NET Framework Data Provider error 1025.
В общем, мои вопросы:
1)любая вложенная в Expression лямбда тоже является экспрешшеном, несмотря на то, что ее тип может быть показан студией, как Func?
2)если да, поэтому мы не можем ее вынести в отдельную переменную, чтобы заюзать в нескольких местах, потому что тогда мы во внешнем экспрешшене используем не лямбду, а ссылку на переменную с лямбдой?
3)как-нибудь можно решить проблему невозможности вынесения вложенной лямбды в отдельную переменную? Объявить переменную как Expression не получается, т.к. Any на ICollection не принимает Expression.