使用DefaultIfEmpty时,C#linq left outer join不起作用

时间:2014-08-25 05:33:42

标签: c# linq

我是c#的新手,我有一个在LINQ中使用左连接的查询。

我有两个数据源 - 一个是类别集合,一个是产品集合。我想将这两个数据源连接在一起,这样我就可以得到这样的结果:

Category Name (Fruit) - Product Name ( peach)- Source ( QLD,NSW).

请注意我在产品系列中有Source as List集合。

我有两个问题:

  1. 当我使用DefaultIfEmpty时,如果Source不存在,我就无法设置对null Souce集合的正确引用 - 但是如果产品名称不存在,我可以引用它有意义的字符串说"不存在"。如果左连接无法找到产品的来源,任何人都可以帮忙展示如何放置空字符串?
  2. 我找不到输出Category Name的方法 - 比如最终结果Fruit ,Vegetables,尽管我可以在group join中进行,但是对于如何做到这一点有什么想法?
  3. 我在这里附上了我的代码。

    class Product
    {
        public string Name { get; set; }
        public int CategoryID { get; set; }
        public List<string> Source;
    }
    
    class Category
    {
        public string Name { get; set; }
        public int ID { get; set; }
    }
    
    // Specify the first data source.
    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.
    List<Product> products = new List<Product>()
    {
         new Product{Name="Cola",CategoryID=001,  Source = new List<string> {"NSW","VIC","QLD"} },  
         new Product{Name="Mustard", CategoryID=002 ,  Source = new List<string> {"NSW","VIC","SA"} },
         new Product{Name="Pickles", CategoryID=002 ,  Source = new List<string> {"NSW","VIC","NT"} },
         new Product{Name="Carrots", CategoryID=003 ,  Source = new List<string> {"NSW","VIC","TAS"} },
         new Product{Name="Bok Choy", CategoryID=003 ,  Source = new List<string> {"NSW","VIC","ACT"} },
         new Product{Name="Eggplant", CategoryID=003 ,  Source = new List<string> {"QLD","NSW"} },
         new Product{Name="Broccoli", CategoryID=003 ,  Source = new List<string> {"QLD,SA"} },
         new Product{Name="Peaches", CategoryID=005 ,  Source = new List<string> {"NSW","VIC","NZ"} },
         new Product{Name="Melons", CategoryID=005,  Source = new List<string> {"NSW","VIC"} },
    };
    

    以下代码不适用于LINQ中的left join

    void LeftOuterJoin()
    {
       var leftOuterQuery =
           from category in categories
           join prod in products on category.ID equals prod.CategoryID into prodGroup          
           select prodGroup.DefaultIfEmpty(new Product()
           { Name = "Nothing!", CategoryID = category.ID})
           ;
    
        // Store the count of total items (for demonstration only). 
        int totalItems = 0;
    
        Console.WriteLine("Left Outer Join:");
    
        // A nested foreach statement  is required to access group items 
        foreach (var prodGrouping in leftOuterQuery)
        {
            Console.WriteLine("\nGroup count: {0}", prodGrouping.Count());
    
            // Intellisense does not show category name  if I use prodGrouping.Name 
             //and want to get categorhy name since left join produce hierarchy data 
             //why category name is not showing up - I am itinerate from 1st loop ??
            foreach (var item in prodGrouping)
            {
                totalItems++;
                Console.WriteLine("  {0,-10}{1}", item.Name, item.CategoryID);
    
                foreach (var s in item.Source)
                {
                    Console.WriteLine("Souce state: {0}", s.ToString());
                }
            }
        }
    
        Console.WriteLine(System.Environment.NewLine);
    }
    

    嗨Marcin,我改变了它的原始代码,但是给了我重复的记录,你知道为什么吗?

     void LeftOuterJoin()
        {
            // Create the query. 
            var leftOuterQuery =
               from category in categories
               join prod in products on category.ID equals prod.CategoryID into prodGroup
               from prodG in prodGroup.DefaultIfEmpty()       
                select new
                {
                    Category = category.Name,
                    //Products = products.OrderBy(x => x.Name)
                    Products = from prod2 in prodGroup
                               orderby prod2.Name
                               select prod2
                };
    
    
            Console.WriteLine("Left Outer Join:");
    
            // A nested foreach statement  is required to access group items 
            foreach (var item in leftOuterQuery)
            {                 
    
                 Console.WriteLine("  {0,-10}", item.Category);
                 foreach ( var p in item.Products)
                 {
                     Console.WriteLine("ProductName: {0}", p.Name);
                     foreach (var s in p.Source)
                     {
                         Console.WriteLine("SourceName: {0}", s.ToString());
    
                     }
                 }                   
    
    
            }
    
        }
    

    我想得到这样的结果:

      Group Beverages:1
      Cola      1
      Souce state: NSW
      Souce state: VIC
      Souce state: QLD
    
     Group : Condiments
      Mustard   2
       Souce state: NSW
       Souce state: VIC
       Souce state: SA
     Pickles   2
      Souce state: NSW
      Souce state: VIC
      Souce state: NT
    

    但我得到了这样的结果:

    Beverages
     ProductName: Cola
     SourceName: NSW
     SourceName: VIC
     SourceName: QLD
    
    Condiments
     ProductName: Mustard
     SourceName: NSW
     SourceName: VIC
     SourceName: SA    
     ProductName: Pickle
     SourceName: NSW
     SourceName: VIC
     SourceName: NT
    
    Condiments
     ProductName: Mustard
     SourceName: NSW
     SourceName: VIC
     SourceName: SA
     ProductName: Pickle
     SourceName: NSW
     SourceName: VIC
     SourceName: NT
    

2 个答案:

答案 0 :(得分:1)

我仍然不确定您要尝试实现的目标,但要使用LINQ获取left outer join,您需要使用join ... intofrom ....DefaultIfEmpty()

您正在使用select ....DefaultIfEmpty(),这并非您所需要的。

var leftOuterQuery =
    from category in categories
    join prod in products on category.ID equals prod.CategoryID into prodGroup
    from products in prodGroup.DefaultIfEmpty()       
    select new
    {
        Category = category.Name,
        Products = products.OrderBy(x => x.Name)
    }

答案 1 :(得分:0)

使用此处发布的linq,左外连接有一个简单而整洁的扩展:http://www.codeproject.com/Tips/724333/Left-Outer-Join-extension-for-Entity-Framework 我想这可能会对你有帮助。