Как правильно использовать GroupBy в LINQ?

У меня есть 4 таблицы: Post, Category, Relation и Meta​​p >

A category может содержать несколько posts, а отношение между ними сохраняется в таблице Relation. В столбце может быть много дополнительной информации, которая хранится в таблице Meta. Я хочу перечислить все сообщения с категориями и дополнительной информацией, а затем группировать их по идентификатору сообщения.

У меня есть следующий запрос

select p.ID, p.Title, t.Name, m.Key, m.Value from Post p
left join Relation r on p.ID = r.Child
left join Category c on r.Parent = c.ID
left join Meta m on p.ID = m.Object
where m.Type = 'news'
order by p.ID

и с этими данными:

Post

ID    Title

1     A

Category

ID    Name

1     Tips
2     Tricks

Meta

ID    Object  Key      Value

1       1     Key1     Value 1
2       1     Key2     Value 2

Relation

ID    Child Parent

1       1     1
2       1     2

тогда результат будет

PostID      Title     Category       Key      Value

  1           A         Tips         Key1     Value1
  1           A         Tips         Key2     Value2
  1           A        Tricks        Key1     Value1
  1           A        Tricks        Key2     Value2

и я ожидал, что результат будет

PostID      Title     Categories               Meta

  1           A      Tips, Tricks    Key1=Value1, Key2=Value2

Интересно, можем ли мы преобразовать запрос из SQL в LINQ to Entities с EF v4, и результат будет сохранен в классе, подобном этому

class Result
{
  long ID,
  string Title,
  List<string> Categories,
  Dictionary<string, string> Meta
}

Любая помощь будет оценена.

Ответы

Ответ 1

Какой конечный результат вы ожидаете от запроса Я лично предпочитаю писать запрос, например

 var q = from r in Relation
         join p in Post on r.Child equals p.ID
         join t in Term on r.Parent equals t.ID
         let x = new { p.ID, p.Title, t.Name }
         group x by x.ID into g
         select g;

таким образом я думаю (не уверен), что сгенерированный sql будет проще

Ответ 2

Теперь, когда вы хотите использовать EntityFramework, вам просто нужно настроить вашу базу данных, edmx с помощью таблицы результатов с идентификатором и титрой, а затем категориями и таблицами Meta. Затем добавьте отношения "один ко многим" из таблицы результатов в таблицы категорий и мета.

Ответ 3

Я не уверен на 100%, что вы пытаетесь сделать, но, очевидно, если вы группируете, результаты должны быть сгруппированы по любой причине в наборе результатов или быть агрегированными данными. Этот запрос будет получать ваши результаты и группу с помощью PostId, PostTitle и CategoryName, генерируя одно выражение SQL:

var query = from p in Posts
from r in Relations
.Where(r => p.ID == r.Child)
.DefaultIfEmpty()
from c in Categories
.Where(c => r.Parent == c.ID)
.DefaultIfEmpty()
group p by new {ID = p.ID, Title = p.Title, Name = c.Name} into z
select new { ID = z.Key.ID, Title = z.Key.Title, Name = z.Key.Name };

Вот SQL, созданный с помощью этого оператора:

SELECT [t3].[ID], [t3].[Title], [t3].[value] AS [Name]
FROM (
SELECT [t0].[ID], [t0].[Title], [t2].[Name] AS [value]
FROM [Post] AS [t0]
LEFT OUTER JOIN [Relation] AS [t1] ON [t0].[ID] = [t1].[Child]
LEFT OUTER JOIN [Category] AS [t2] ON [t1].[Parent] = [t2].[ID]
) AS [t3]
GROUP BY [t3].[ID], [t3].[Title], [t3].[value]

Вот SQL, сгенерированный вашим исходным выражением:

 SELECT [t0].[ID] AS [Key]
FROM [Post] AS [t0]
INNER JOIN [Relation] AS [t1] ON [t0].[ID] = [t1].[Child]
INNER JOIN [Category] AS [t2] ON [t1].[Parent] = [t2].[ID]
    GROUP BY [t0].[ID]
GO

-- Region Parameters
DECLARE @x1 Int SET @x1 = 1
-- EndRegion
SELECT [t0].[ID], [t0].[Title], [t2].[Name]
FROM [Post] AS [t0]
INNER JOIN [Relation] AS [t1] ON [t0].[ID] = [t1].[Child]
INNER JOIN [Category] AS [t2] ON [t1].[Parent] = [t2].[ID]
WHERE ((@x1 IS NULL) AND ([t0].[ID] IS NULL)) OR ((@x1 IS NOT NULL) AND         ([t0].[ID]         IS     NOT NULL) AND (@x1 = [t0].[ID]))
    GO

-- Region Parameters
DECLARE @x1 Int SET @x1 = 2
-- EndRegion
SELECT [t0].[ID], [t0].[Title], [t2].[Name]
FROM [Post] AS [t0]
INNER JOIN [Relation] AS [t1] ON [t0].[ID] = [t1].[Child]
INNER JOIN [Category] AS [t2] ON [t1].[Parent] = [t2].[ID]
WHERE ((@x1 IS NULL) AND ([t0].[ID] IS NULL)) OR ((@x1 IS NOT NULL) AND ([t0].[ID] IS     NOT NULL) AND (@x1 = [t0].[ID]))