Это немного сложно, но вы можете сделать это одним большим запросом, если у вас есть возможность создавать новые экземпляры ImageTag
и Tag
для работы с linq. По сути, когда вы выполняете внешнее соединение, вы должны использовать ключевое слово into
с методом DefaultIfEmpty(...)
, чтобы справиться с «пробелами внешнего соединения» (например, когда правая сторона соединенного ключа равна нулю в типичное SQL левое внешнее соединение).
var images = from img in db.Images
join imgTags in db.ImageTags on img.idImage equals imgTags.idImage
into outerImageRef
from outerIR in outerImageRef.DefaultIfEmpty(new ImageTag() { idImage = img.idImage, idTag = -1 })
join t in db.Tags on imgTags.idTag equals t.idTag
into outerRefTags
from outerRT in outerRefTags.DefaultIfEmpty(new Tag(){ idTag=-1, TagName ="untagged"})
group img by outerRT.TagName into aGroup
select new {
GroupName = aGroup.Key,
Items = from x in aGroup
select new ImageFragment() {
ImageID = x.idImage,
ScanDate = x.ScanTime
}
};
Надеюсь, что приведенные выше компилируются, поскольку у меня нет вашей точной среды, я построил свое решение, используя свои собственные типы данных, а затем преобразовал его в описание вашего вопроса. По сути, ключевыми частями являются дополнительные строки into
и DefaultIfEmpty
, которые по существу помогают добавить дополнительные "строки" в массивную таблицу, находящуюся в памяти, если вы думаете об этом в традиционном смысле sql.
Однако есть более удобочитаемое решение, которое не требует инстанцирования в памяти сущностей linq (вам придется преобразовать это самостоятельно в вашу среду):
//this first query will return a collection of anonymous types with TagName and ImageId,
// essentially a relation from joining your ImageTags x-ref table and Tags so that
// each row is the tag and image id (as Robert Harvey mentioned in his comment to your Q)
var tagNamesWithImageIds = from tag in Tags
join refer in ImageTags on tag.IdTag equals refer.IdTag
select new {
TagName = tag.Name,
ImageId = refer.IdImage
};
//Now we can get your solution by outer joining the images to the above relation
// and filling in the "outer join gaps" with the anonymous type again of "untagged"
// and then joining that with the Images table one last time to get your grouping and projection.
var images = from img in Images
join t in tagNamesWithImageIds on img.IdImage equals t.ImageId
into outerJoin
from o in outerJoin.DefaultIfEmpty(new { TagName = "untagged", ImageId = img.IdImage })
join img2 in Images on o.ImageId equals img2.IdImage
group img2 by o.TagName into aGroup
select new {
TagName = aGroup.Key,
Images = aGroup.Select(i => i.Data).ToList() //you'll definitely need to replace this with your code's logic. I just had a much simpler data type in my workspace.
};
Надеюсь, это имеет смысл.
Конечно, вы всегда можете просто установить в своем приложении тег по умолчанию на все без тегов или выполнить несколько более простых запросов LINQ, чтобы создать список идентификаторов изображений, которых нет в вашей таблице ImageTag, а затем объединить или что-то еще.