【问题标题】:How to use LINQ to return a query result to a dictionary如何使用 LINQ 将查询结果返回到字典
【发布时间】:2012-04-16 08:30:02
【问题描述】:

我正在开发一个 MVC 项目。我需要返回一个对象,该对象将菜单项分组为四个一组,以使用 foreach 迭代在视图中显示。由于这是一个传递给视图的模型,我需要返回一个可以绑定在 .cshtml 上的非泛型集合类。

我的想法是,我可以根据 LINQ 中的某种类型的计数对列表进行分组,以便为​​每个 Dictionary 键添加四个新的列表项。但是,我还没有找到一个足以满足字典的表达式。我可以通过创建另一种方法并再次遍历集合以根据计数分配键来相当容易地解决这个问题,但这似乎是不必要的迭代和不好的做法。

目前,我正在处理以下查询。

        var itemsDict = rssDataContext.rss_Application_Blog_Category_Relationships
            .Where(x => x.Application_Blog.rss_Application.ID == 1)
            .Where(x => x.Blog_Category_ID == 1)
            .Select(x => new MenuItem
            {
            Name = x.rss_Application_Blog.Blog_Title,
            Uri = x.rss_Application_Blog.Blog_Uri,
            ID = x.rss_Application_Blog.ID
            });

但是有没有办法将此查询分组为 4 个类的组,然后填充到 Dictionary> 中?

【问题讨论】:

  • 我不清楚这里的关键是什么。我确信 ToDictionary(或 ToLookup)会让这变得简单,但是如果不了解您要做什么,就很难举个例子。 “某种类型的计数”非常模糊。
  • 本质上,我需要将查询分组为 4 个组,因此每个子列表中包含 4 个列表项,但我无法将其返回为泛型类型。因此,它必须返回一个包含许多子列表的集合类型。
  • “由于这是一个传递给视图的模型,我需要返回一个非泛型集合类”为什么?您可以对视图使用泛型类型(只要它们未打开,例如您可以使用 IGrouping<int,List<MenuItem>> 但不能使用 IGrouping<TKey,TElement>
  • 您希望对哪个属性进行分组?你说某种计数,但计数什么?
  • 这是一个有效的观点。但是,我仍然需要一个可以填充 IGrouping> 的查询。

标签: c# asp.net-mvc linq


【解决方案1】:

我怀疑你可以这样做:

var itemsDict = rssDataContext.rss_Application_Blog_Category_Relationships
    .Where(x => x.Application_Blog.rss_Application.ID == 1)
    .Where(x => x.Blog_Category_ID == 1)
    .Select(x => new { 
        x.rss_Application_Blog.BlogTitle,
        x.rss_Application_Blog.BlogUri,
        x.rss_Application_Blog.ID
    })
    .AsEnumerable() // Do the rest of the query in-process
    .Select((value, index) => new { value, index })
    .ToLookup(pair => pair.index % 4,
              pair => new MenuItem {
                 Name = pair.value.Blog_Title,
                 Uri = pair.value.Blog_Uri,
                 ID = pair.value.ID
              });

请注意,这会返回 Lookup 而不是 Dictionary,但这实际上是您想要的更接近的模型。

【讨论】:

  • 不幸的是,上面的查询由于不支持的选择运算符重载而引发了 NotSupportedException。
  • 这是因为rss_Application_Blog_Category_Relationships 属性返回的类型是IQueryable 并且底层的LINQ 提供程序不支持投影。在应用Select 方法之前,您必须通过添加ToArray()ToList() 将源序列加载到内存中。例如:.Where(x => x.Blog_Category_ID == 1).ToArray().Select(...)
【解决方案2】:

@JonSkeet 的解决方案非常好。这是一个替代实现,使用GroupBy 创建子列表并使用ToDictionary 将它们投影到最终结果中:

var itemsDict = rssDataContext.rss_Application_Blog_Category_Relationships
    .Where(x => x.Application_Blog.rss_Application.ID == 1)
    .Where(x => x.Blog_Category_ID == 1)
    .ToArray()
    .Select((x, index) => new
        {
            Index = index,
            Item = new MenuItem
            {
                Name = x.rss_Application_Blog.Blog_Title,
                Uri = x.rss_Application_Blog.Blog_Uri,
                ID = x.rss_Application_Blog.ID
            }
        });
    .GroupBy(i => i.Index / 4)
    .ToDictionary(g => g.Key);

生成的序列将如下所示:

items = new Dictionary<int, IGrouping<int>>()
{
    0, new[]
    {
        new { Index = 0, Item = new MenuItem { ... } },
        new { Index = 1, Item = new MenuItem { ... } }
    }
    1, new[]
    {
        new { Index = 2, Item = new MenuItem { ... } },
        new { Index = 3, Item = new MenuItem { ... } }
    }
};

【讨论】:

  • 感谢大家的帮助。不过,这也会引发 NotSupportedException。
  • 你是在最后一个Where(...)方法之后添加ToArray()还是ToList()
猜你喜欢
  • 2012-11-07
  • 1970-01-01
  • 2010-10-31
  • 2016-09-30
  • 1970-01-01
  • 2014-12-07
  • 2010-09-25
  • 1970-01-01
  • 2017-01-18
相关资源
最近更新 更多