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

C# 在简单/未命名C LINQ组联接中对内部键源排序,c#,linq,join,anonymous-types,C#,Linq,Join,Anonymous Types,我知道这个问题读起来很奇怪,因为用了很多词。但这是我在学习LINQ group加入时使用的术语,我将解释它们 我正在尝试LINQ的数据是: class Product { public string Name { get; set; } public int CategoryID { get; set; } } class Category { public string Name { get; set;

我知道这个问题读起来很奇怪,因为用了很多词。但这是我在学习LINQ group加入时使用的术语,我将解释它们

我正在尝试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},
    };
而不是选择匿名类型或使用嵌套的from select子句

LINQ组联接中的外部键源是跟随关键字的数据源,内部键源是跟随join关键字的数据源

为了方便起见,我将简单组join命名为unnamed,因为我们在所选组中没有外部键源名称/Id

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

Unnamed Group
  Cola
  Tea
Unnamed Group
  Mustard
  Pickles
Unnamed Group
  Melons
  Peaches
Unnamed Group
Unnamed Group
  Bok Choy
  Carrots
我可以按以下方式通过外部keysource订购:

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);
    }
}
        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);
            }
        }
但它正在产生以下产出:

Unnamed group
  Cola
  Tea
Unnamed group
  Mustard
  Pickles
Unnamed group
  Peaches
  Melons
Unnamed group
Unnamed group
  Carrots
  Bok Choy
但我无法订购未命名类别组下的产品

我知道我可以通过如下方式选择匿名类型来实现这一点:

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);
    }
}
        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);
            }
        }
但是,我只想知道我是否可以在不选择匿名类型的情况下执行相同的操作。我觉得它在语法上应该与keysources和选择匿名类型上的顺序无关。因此,在查询本身中应该有如下方法来实现这一点,但它不起作用:

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;

我可能错了。但我认为如果没有完整的选择,你不可能做到这一点。我有点不同意匿名类型的选择和orderby是无关的

要么选择查询中已经提到的对象,在这种情况下不需要anon类型,要么根据它选择其他对象。如果您选择了其他内容,而不是查询前一部分给出的内容,则必须进行完全选择

IINM这在SQL中是相同的-您需要一个子查询来执行类似的操作,而不是像这样选择整个组,而是如果您想执行ORDER BY后跟TOP的操作。

您可以在加入之前订购产品:

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保留了outer元素的顺序,而对于这些元素中的每一个,都保留了internal元素的匹配顺序

有些备选方案更像您的示例:

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); 

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

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;

                }
            }

您的收藏已经在内部订购,这与此有多大关系?您可以在迭代组键和/或分组项时轻松地对它们进行排序。只有当您需要一次又一次地迭代同一个有序集合时,我才能想象预排序会带来性能上的好处。如果说您可以在这里选择一个属性,产品名称,但不能按它排序,那么不使用扩展方法就无法应用排序,正如我在问题的最后一段代码摘录中所示,甚至作为orderby category.Name,product.Name?@Mahesha999您的最后一段代码事实上,您问题中的每段代码摘录都使用扩展方法.yep,但放入明确的orderby product.Name会出现错误。当前上下文中不存在产品名称,我无法找到任何使用LINQ应用排序的方法,现在我意识到显式使用OrderBy扩展方法是唯一的解决方案。是这样吗?@Mahesha999好吧,你用OrderBy命令一个序列,然后Join连接两个序列;而且我认为在加入之前订购产品没有任何问题,因为您在加入之前也已经订购了类别。请记住,编译器会将使用simple的查询语法转换为方法语法,因此,如果将产品转换为方法语法,则不能简单地插入orderby product.Name。尽管如此,我还是添加了一些示例来对加入后的组进行排序,这些示例看起来更像您的示例。@Mahesha999是的,使用OrderBy。。。或者填写类似于。。。进入prodGroup在prodGroup orderby p中从p中选择。名称选择p是唯一的方法。但是,您当然可以编写自己的GroupBy扩展方法,该方法不依赖于隐式排序本身,甚至可以使用查询语法:-