没有结果时LINQ to SQL连接

时间:2010-04-02 02:00:29

标签: c# join linq-to-sql linq-group

给出以下数据库结构 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
        }
    };

哪个效果很好。但是,我还想在一组“(未标记)”或其他内容中返回没有与之关联的任何标记的图像。如果不为每个图像插入默认标签,我无法理解如何做到这一点,这似乎不是一个非常好的解决方案。

3 个答案:

答案 0 :(得分:1)

如果您在没有相应的标记记录时想要图像记录,则需要在图像标记表上执行左outer join

答案 1 :(得分:0)

这有点棘手,但如果您能够为linq实例化新的ImageTagTag实例,则可以在一个大查询中执行此操作。基本上,当您进行外连接时,必须使用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
             }
     };

希望以上编译因为我没有您的确切环境,我使用自己的数据类型构建了我的解决方案,然后将其转换为您的问题描述。基本上关键部分是额外的intoDefaultIfEmpty行,如果你在传统的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
                                        }
                    };