在简单/未命名的C#LINQ组连接中排序内部键源

时间:2014-05-30 11:00:06

标签: c# linq join anonymous-types

我知道由于使用了单词,这个问题很奇怪。但这是this MSDN page上用来学习LINQ组连接的术语,我将解释它们。

我正在尝试LINQ的数据是:

    class Product
    {
        public string Name { get; set; }
        public int CategoryID { get; set; }
    }

    class Category
    {
        public string Name { get; set; }
        public int ID { get; set; }
    }

    // Specify the first data source.
    static List<Category> categories = new List<Category>()
    { 
        new Category(){Name="Beverages", ID=001},
        new Category(){ Name="Condiments", ID=002},
        new Category(){ Name="Vegetables", ID=003},
        new Category() {  Name="Grains", ID=004},
        new Category() {  Name="Fruit", ID=005}            
    };

    // Specify the second data source.
    static List<Product> products = new List<Product>()
    {
        new Product{Name="Cola",  CategoryID=001},
        new Product{Name="Tea",  CategoryID=001},
        new Product{Name="Mustard", CategoryID=002},
        new Product{Name="Pickles", CategoryID=002},
        new Product{Name="Carrots", CategoryID=003},
        new Product{Name="Bok Choy", CategoryID=003},
        new Product{Name="Peaches", CategoryID=005},
        new Product{Name="Melons", CategoryID=005},
    };

现在的条款:

  • 简单/未命名的群组加入是我们直接选择群组的协议:

    //... 
    join product in products on category.ID equals product.CategoryID into prodGroup                      
            select prodGroup;
    

    而不是选择匿名类型或使用嵌套的from-select子句。

  • LINQ group join中的
  • 外键源是跟随from关键字的数据源,内部密钥源是跟随join的数据源关键字

    我称简单的群组加入未命名(为了方便起见),因为我们在所选群组中没有外部密钥源名称/ ID。

我试图编写简单/未命名的查询,它将生成以下结果,首先是orderby外键源属性,然后是内部keyource属性:

Unnamed Group
  Cola
  Tea
Unnamed Group
  Mustard
  Pickles
Unnamed Group
  Melons
  Peaches
Unnamed Group
Unnamed Group
  Bok Choy
  Carrots

我可以通过外键源订购如下:

var simpleGroupJoinOrderby = 
        from category in categories
        orderby category.Name           //orderby outer ks
        join product in products on category.ID equals product.CategoryID into prodGroup                      
        select prodGroup;

foreach (var unnamedGroup in simpleGroupJoinOrderby)
{   
    Console.WriteLine("Unnamed group");
    foreach(var product in unnamedGroup)
    {
        Console.WriteLine("  " + product.Name);
    }
}

但它正在产生以下输出:

Unnamed group
  Cola
  Tea
Unnamed group
  Mustard
  Pickles
Unnamed group
  Peaches
  Melons
Unnamed group
Unnamed group
  Carrots
  Bok Choy

但我无法在未命名的类别组下订购产品。

我知道我可以通过选择匿名类型来完成此操作:

        var namedGroupJoinOrderBy =
            from category in categories
            orderby category.Name  //order group hierarchy by name
            join product in products on category.ID equals product.CategoryID into prodGroup
            select
            new
            {
                Category = category.Name,
                Products = from prod in prodGroup
                           orderby prod.Name       //order by prodGroup.prod.Name under particular group hierarchy
                           select prod
            };

        foreach (var category in namedGroupJoinOrderBy)
        {
            Console.WriteLine("Category : " + category.Category);
            foreach (var product in category.Products)
            {
                Console.WriteLine("   " + product.Name);
            }
        }

但是我只是想知道如果不选择匿名类型我是否可以这样做。 我觉得它在语法上与“两个”键源的顺序和选择匿名类型无关。 因此在查询本身应该有一些方法可以做到这一点如下,但它不起作用:

var simpleGroupJoinOrderby = 
            from category in categories
            orderby category.Name           //orderby outer ks
            join product in products on category.ID equals product.CategoryID into prodGroup    
            orderby product.Name   //Err: The name 'product' does not exist in the current context
            select prodGroup;

3 个答案:

答案 0 :(得分:2)

您可以在加入之前订购products

var simpleGroupJoinOrderby = 
    from category in categories
    orderby category.Name     
    join product in products.OrderBy(p => p.Name) 
        on category.ID equals product.CategoryID into prodGroup                      
    select prodGroup;

结果是:

Unnamed group
  Cola
  Tea
Unnamed group
  Mustard
  Pickles
Unnamed group
  Melons
  Peaches
Unnamed group
Unnamed group
  Bok Choy
  Carrots

它起作用是因为Join保留了外部元素的顺序,并且对于每个元素,内部匹配元素的顺序(引自MSDN)。


一些替代方案(更像您的例子):

var simpleGroupJoinOrderby = categories.OrderBy (c => c.Name)
                                       .GroupJoin(products, 
                                                  c => c.ID, 
                                                  p => p.CategoryID, 
                                                  (c, ps) => ps.OrderBy(p => p.Name));

var simpleGroupJoinOrderby = from category in categories
                             orderby category.Name     
                             join product in products
                                on category.ID equals product.CategoryID into prodGroup                      
                             select prodGroup.OrderBy(g => g.Name); 

答案 1 :(得分:0)

我可能是错的。但我不认为你可以在没有完整select的情况下做到这一点。我有点不同意匿名类型和order by的选择是不相关的。

select查询中已经提到的那个东西,在这种情况下你不需要一个匿名类型,或者你根据它选择其他东西。如果你select有其他内容,而不是查询前一部分给出的内容,则必须完整select

IINM这在SQL中是相同的 - 你需要一个子查询来做类似的事情(不是你要像这样选择整个组,但是如果你想做ORDER BY后跟{ {1}})。

答案 2 :(得分:0)

我查看了您提供的链接中列出的代码,这是您问题的解决方案。您在查询中的原始订单只会订购类别,不会查看产品。如果您需要在问题中描述的订单,则还需要另一个子查询来订购产品:

var simpleGroupJoinOrderby =
        from category in DBContext.categories
        orderby category.Name           //orderby outer ks
        join product in DBContext.products on category.ID equals product.CategoryID into prodGroup
        select prodGroup;

            foreach (var unnamedGroup in simpleGroupJoinOrderby)
            {
                lblList.Text += "//Unnamed group";
                var unnamedProductsGroup = unnamedGroup.OrderBy(z => z.Name).Select(group => new { name = group.Name, ID = group.CategoryID });

                foreach (var product in unnamedProductsGroup)
                {
                    //Console.WriteLine("  " + product.Name);
                    lblList.Text += "|" + product.name;

                }
            }
相关问题