Как правильно использовать GroupBy в LINQ?
У меня есть 4 таблицы: Post, Category, Relation и Metap >
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]))