2015-03-29 4 views
3

Это вопрос, на который я не могу ответить. В настоящее время у меня есть следующий простой фрагмент кода.Можете ли вы использовать прогнозы на свойства навигации Entity Framework?

return _context.UserProfiles 
       .Single(p => p.ID == userID) 
        .Organisations 
        .Select(p => new { ID = p.ID }) 
        .First() 
        .ID; 

Это просто получает первый профиль (который втягивает в SELECT * FROM UserProfiles WHERE ...), который прекрасно. Проблема заключается в свойствах навигации Организации.

Я использую сначала код с миграциями, поэтому между UesrProfiles и организациями (многие или многие) существует таблица соединений, которая автоматически создается EF. В итоге у меня появилось свойство навигации по пользовательскому профилю для организаций и свойства навигации для стран-членов организации. Так легко.

Моя проблема с организацией nav nav заключается в том, что она не проецируется. При выборе и первом я получаю следующий SQL, отправленный на сервер БД.

SELECT 
    [Extent2].[ID] AS [ID], 
    [Extent2].[Description] AS [Description], 
    [Extent2].[ShortDescription] AS [ShortDescription], 
    [Extent2].[Name] AS [Name], 
    [Extent2].[Address] AS [Address], 
    [Extent2].[City] AS [City], 
    [Extent2].[State] AS [State], 
    [Extent2].[PostCode] AS [PostCode], 
    [Extent2].[Country] AS [Country], 
    [Extent2].[Created] AS [Created], 
    [Extent2].[CreatedBy] AS [CreatedBy] 
    FROM [dbo].[OrganisationMembers] AS [Extent1] 
    INNER JOIN [dbo].[Organisations] AS [Extent2] ON [Extent1].[Organisation_ID] = [Extent2].[ID] 
    WHERE [Extent1].[UserProfile_ID] = @EntityKeyValue1 

Поскольку я проектирую только идентификатор, не должен ли я получать следующее?

SELECT 
    [Extent2].[ID] AS [ID] 
    FROM [dbo].[OrganisationMembers] AS [Extent1] 
    INNER JOIN [dbo].[Organisations] AS [Extent2] ON [Extent1].[Organisation_ID] = [Extent2].[ID] 
    WHERE [Extent1].[UserProfile_ID] = @EntityKeyValue1 

Соединение правильное, как я ожидаю, но не столбцы. Я думаю, я мог бы создать еще один объект для таблицы соединений, а затем написать код, соединяющий и выбирающий вручную, но это то, что я ожидал бы от EF для меня.

UPDATE

Я просто не играл с упорядочением и проекций исходного профиля пользователя привезенные из First(). Если я изменить код для этого:

return _context.UserProfiles 
    .Select(p => new { ID = p.ID, Organisations = p.Organisations.Select(q => new { ID = q.ID }) }) 
    .Single(p => p.ID == userID) 
    .Organisations 
    .First() 
    .ID; 

Я заканчиваю с этим уродством:

SELECT 
    [Project1].[ID] AS [ID], 
    [Project1].[C1] AS [C1], 
    [Project1].[Organisation_ID] AS [Organisation_ID] 
    FROM (SELECT 
     [Limit1].[ID] AS [ID], 
     [Extent2].[Organisation_ID] AS [Organisation_ID], 
     CASE WHEN ([Extent2].[Organisation_ID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1] 
     FROM (SELECT TOP (2) [Extent1].[ID] AS [ID] 
      FROM [dbo].[UserProfiles] AS [Extent1] 
      WHERE [Extent1].[ID] = @p__linq__0) AS [Limit1] 
     LEFT OUTER JOIN [dbo].[OrganisationMembers] AS [Extent2] ON [Limit1].[ID] = [Extent2].[UserProfile_ID] 
    ) AS [Project1] 
    ORDER BY [Project1].[ID] ASC, [Project1].[C1] ASC 

Но я предполагаю, что это проецирование своих идентификаторов, как я хочу.

UPDATE 2

С ответом от Gongdo, я обновил, чтобы иметь следующее:

return _context.UserProfiles 
        .Where(p => p.ID == userID) 
        .Take(1) 
        .SelectMany(p => p.Organisations) 
        .Select(p => new { ID = p.ID }) 
        .First().ID; 

который выводит:

SELECT 
    [Limit2].[Organisation_ID] AS [Organisation_ID] 
    FROM (SELECT TOP (1) 
     [Extent2].[Organisation_ID] AS [Organisation_ID] 
     FROM (SELECT TOP (1) [Extent1].[ID] AS [ID] 
      FROM [dbo].[UserProfiles] AS [Extent1] 
      WHERE [Extent1].[ID] = @p__linq__0) AS [Limit1] 
     INNER JOIN [dbo].[OrganisationMembers] AS [Extent2] ON [Limit1].[ID] = [Extent2].[UserProfile_ID] 
    ) AS [Limit2] 

я гораздо ближе к выход, который я хочу. Спасибо за это.

ответ

1

Single, SingleOrDefault, First и FirstOrDefault - сами методы проектирования. Поэтому вам нужно сохранить IQueryable, пока вам не понадобится проецировать его. Следующее будет работать так, как вы хотите.

return _context.UserProfiles 
       .Where(p => p.ID == userID) 
       .SelectMany(p => p.Organisations) 
       .Select(p => new { ID = p.ID }) 
       .First() 
       .ID; 
+0

Благодарим за ответ. Результат не был тем, что я ожидал, но он, вероятно, лучше, чем то, что я бы написал в SQL, используя несколько внутренних объединений. Очень признателен! : D –

+0

О, ну, вам нужен один из UserProfiles. Используйте 'Take (1)' after 'Where'. Это ваше решение? Я надеюсь, что это так. –

+0

Если вы используете take (1), он фактически выполнит несколько подзапросов с внутренним соединением.Я обновил вопрос с помощью запроса и вывода. Это помогает намного больше, я буду проверять его на более сложных Iqueryables завтра на работе, но по большей части я думаю, что это решит мою проблему в промежутке. –