给出以下数据库结构 alt text http://dl.dropbox.com/u/26791/tables.png
我正在尝试编写一个LINQ查询,该查询将返回按照与之关联的标记分组的图像。到目前为止,我有这个:
var images = from img in db.Images
join imgTags in db.ImageTags on img.idImage equals imgTags.idImage
join t in db.Tags on imgTags.idTag equals t.idTag
where img.OCRData.Contains(searchText.Text)
group img by new { t.TagName } into aGroup
select new
{
GroupName = aGroup.Key.TagName,
Items = from x in aGroup
select new ImageFragment()
{
ImageID = x.idImage,
ScanDate = x.ScanTime
}
};
哪个效果很好。但是,我还想在一组“(未标记)”或其他内容中返回没有与之关联的任何标记的图像。如果不为每个图像插入默认标签,我无法理解如何做到这一点,这似乎不是一个非常好的解决方案。
答案 0 :(得分:1)
如果您在没有相应的标记记录时想要图像记录,则需要在图像标记表上执行左outer join。
答案 1 :(得分:0)
这有点棘手,但如果您能够为linq实例化新的ImageTag
和Tag
实例,则可以在一个大查询中执行此操作。基本上,当您进行外连接时,必须使用into
关键字和DefaultIfEmpty(...)
方法来处理“外连接间隙”(例如,当连接键的右侧时)在典型的SQL左外连接中为null。
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意义上考虑它,本质上有助于将额外的“行”添加到内存中的大量连接表中。 / p>
然而,有一个更易读的解决方案,不需要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. };
希望这是有道理的。 当然,您可以随时将应用程序设置为默认情况下使用“untagged”标记所有内容,或者执行更简单的LINQ查询以创建ImageTag表中不存在的图像ID列表,然后使用union或其他内容。 / p>
答案 2 :(得分:0)
这就是我最终做的事情。我还没有检查过它产生了什么样的SQL,我猜它可能不是很漂亮。我想我最好自己做一些查询并汇总这些东西,但无论如何这都有效:
var images = from img in db.Images
join imgTags in db.ImageTags on img.idImage equals imgTags.idImage into g
from imgTags in g.DefaultIfEmpty()
join t in db.Tags on imgTags.idTag equals t.idTag into g1
from t in g1.DefaultIfEmpty()
where img.OCRData.Contains(searchText.Text)
group img by t == null ? "(No Tags)" : t.TagName into aGroup
select new
{
GroupName = aGroup.Key,
Items = from x in aGroup
select new ImageFragment()
{
ImageID = x.idImage,
ScanDate = x.ScanTime
}
};