2011-12-30 88 views
1

我們的初始查詢由許多基於使用單列(productId)的連接正常工作的子查詢組成。產生的模型映射到一個網格,該網格列出了昨天,今天和明天的產品名稱及其各自所需的數量。LINQ多列加入

但是,基於產品的使用年限收到了額外差異因素的要求,因此需要修改原始查詢。

因此,以下代碼是使用單個字段(ProductId作爲關鍵字)的工作代碼的修改。在試圖修改查詢使用多列密鑰(產品編號及年齡)我遇到了麻煩,接收以下錯誤:

The type of one of the expressions in the join clause is incorrect. Type inference failed in the call to 'GroupJoin'.

在創建不同的集合前面的查詢,我改變了關鍵的複合ProductId和年齡,並將新的匿名類型記憶體productKey設置爲新{pr.productId,pr.age}。然後在最終查詢中,我試圖在productKey上等待新結果等於new {y.productId,y.age}(y表示加入結果「昨天」)。

當我將鼠標懸停在每個連接結果(gr.productKey和y.productKey)的鍵的,下面是顯示每個:

'b 'a.productKey

Anonymous Types:

'a is new {'b productKey, int productId, string age,... }

'b is new {int productId, string age}

由於兩者都是類型的「B一個new {int productId,string age}我期待成功;然而,編譯器仍然不合作。我相信新的{int,string}是另一個無名的名字。

var yesterday = from p in productOrdered 
       where p.deliveryDate.Date == DateTime.Now.AddDays(-1).Date 
       group p by new { p.id, p.age } into g 
       orderby g.Key 
       select new { 
        productKey = g.Key, 
        productId = g.Max(s => s.id), 
        age = g.Max(s => s.age), 
        quantity = g.Count(), 
        weight = g.Sum(s => s.weight), 
       }; 

var grp = (from pr in prods2 
      group pr by new { pr.productId, pr.age } into g 
      orderby g.Key 
      select new { 
       productKey = g.Key, 
       productId = g.Max(s => s.productId), 
       age = g.Max(s => s.age), 
       code = g.Max(s => s.code), 
       product = g.Max(s => s.product), 
       uom = g.Max(s => s.uom) 
      }).Distinct(); 

var model = from gr in grp 
      join y in yesterday on gr.productKey equals new { y.productId, y.age } into outer0 
      from y in outer0.DefaultIfEmpty() 
      join n in now on gr.productKey equals new { n.productId, n.age } into outer1 
      from n in outer1.DefaultIfEmpty() 
      join t in tomorrow on gr.productKey equals new { t.productId, t.age } into outer2 
      from t in outer2.DefaultIfEmpty() 
      select new RequiredProductsViewModel 
      { 
       ProductId = gr.productId, 
       Aged = gr.age, 
       Code = gr.code.ToString(), 
       Description = gr.product.ToString(), 
       MinusQ = (!(null == y) ? y.quantity : 0), 
       MinusW = (!(null == y) ? decimal.Parse(y.weight.ToString()) : 0), 
       ZeroQ = (!(null == n) ? n.quantity : 0), 
       ZeroW = (!(null == n) ? decimal.Parse(n.weight.ToString()) : 0), 
       OneQ = (!(null == t) ? t.quantity : 0), 
       OneW = (!(null == t) ? decimal.Parse(t.weight.ToString()) : 0), 
       UofM = gr.uom.ToString() 
      }; 

測試在LINQPad導致類似的結果,我也試了幾個變化基於這樣在該網站類似的問題,但不限於以下內容:

join y in yesterday on new {Key1 = gr.productId,Key2 = gr.age} equals y.productKey into outer0

join y in yesterday on new { gr.productId, gr.age } equals y.productKey into outer0

此外,原來的查詢,這修改工作順利進行。我很確定這是「有一點知識,是一件危險的事情」之一。或者也許只是「小知識」問題。無論哪種方式,我希望LINQ Gods能夠看到一個解決方案。對於多列鍵,而不是使用匿名

回答

0

嘗試聲明一個名爲類型:

public class ProductKey 
{ 
    public int ProductId { get; set; } 

    public int ProductAge { get; set; } 
} 

使用此的ProductKey在「分組依據」和「加盟」的條款。 所以,你的查詢會是這樣的:

   var yesterday = from p in productOrdered 
       where p.deliveryDate.Date == DateTime.Now.AddDays(-1).Date 
       group p by new ProductKey { ProductId=p.id, ProductAge=p.age } into g 
       orderby g.Key.ProductId 
       select new { 
        productKey = g.Key, 
        productId = g.Max(s => s.id), 
        age = g.Max(s => s.age), 
        quantity = g.Count(), 
        weight = g.Sum(s => s.weight), 
       }; 

var grp = (from pr in prods2 
      group pr by new ProductKey{ ProductId=pr.productId, ProductKey=pr.age } into g 
      orderby g.Key.ProductId 
      select new { 
       productKey = g.Key, 
       productId = g.Max(s => s.productId), 
       age = g.Max(s => s.age), 
       code = g.Max(s => s.code), 
       product = g.Max(s => s.product), 
       uom = g.Max(s => s.uom) 
      }).Distinct(); 

var model = from gr in grp 
      join y in yesterday on gr.productKey equals new ProductKey { ProductId=y.productId, ProductAge=y.age } into outer0 
      from y in outer0.DefaultIfEmpty() 
      join n in now on gr.productKey equals new ProductKey { ProductId=n.productId, ProductAge=n.age } into outer1 
      from n in outer1.DefaultIfEmpty() 
      join t in tomorrow on gr.productKey equals new ProductKey { ProductId=t.productId, ProductAge=t.age } into outer2 
      from t in outer2.DefaultIfEmpty() 
      select new RequiredProductsViewModel 
      { 
       ProductId = gr.productId, 
       Aged = gr.age, 
       Code = gr.code.ToString(), 
       Description = gr.product.ToString(), 
       MinusQ = (!(null == y) ? y.quantity : 0), 
       MinusW = (!(null == y) ? decimal.Parse(y.weight.ToString()) : 0), 
       ZeroQ = (!(null == n) ? n.quantity : 0), 
       ZeroW = (!(null == n) ? decimal.Parse(n.weight.ToString()) : 0), 
       OneQ = (!(null == t) ? t.quantity : 0), 
       OneW = (!(null == t) ? decimal.Parse(t.weight.ToString()) : 0), 
       UofM = gr.uom.ToString() 
      }; 

UPDATE:

的ORDER BY用的ProductKey類節將給出一個錯誤(LINQ不知道如何訂購多列類),所以您應該通過g.Key.ProductId具體爲

+0

當我進行上述更改時,出現錯誤消息,指出「至少有一個對象必須實現IComparable。」這顯示在上述所有查詢中,「昨天」,「grp」和「模型」以及未顯示的那些查詢(「現在」和「明天」),但是與查詢「昨天」相同。 – 2011-12-30 08:02:35

+0

@ user946045:IComparable定義了CompareTo方法,它告訴如何比較兩個類的實例。這是訂購所必需的,但在這種情況下,我認爲,最好是按特定列排序 - 更新 – PanJanek 2011-12-30 08:08:27

+0

到目前爲止,創建productKey類看起來好像向後退出了2步。當我對最後一個查詢中的所有查詢使用匿名類型時,每一步都會得到準確的結果。當我使用一個定義的類時,我得到一個IComparable錯誤,我無法使用Order By子句。 – 2011-12-30 08:40:13

0

以下更改似乎產生了所需的結果。

  1. 在從屬查詢分組改變爲新{p.id,p.ageId}
  2. 所有的OrderBy子句從單一的OrderBy進行了修訂基於g.Key到2個獨立的OrderBy子句基於g.Key.idg.Key.ageId
  3. 最後,在查詢定義表,我用了以下內容:

group new { pr.code, pr.product, pr.uom} by new { pr.productId, pr.ageId} into g

我以前在另一種方法中成功地使用了這種變體,但忘記了我遇到它的地方。它當然可以精確地定義字段和複合鍵。

此方法現在生成訂購產品的合計清單,其中包含數量和重量的總計。此外,不同年齡要求的產品將單獨列出。最終,我們會得到一份產品清單,僅顯示已訂購的產品,按年齡分組並顯示數量和重量,過期訂單,今天訂單和明天訂單。

我已經將這種方法的所有代碼都包含在內,作爲對某些人的幫助,並且對於那些有更高技能的人來說可以找到改善。

[GridAction] 
    public ActionResult AjaxOps_ActionList() { 

     var orders = salesOrderHeaderRepository.All.ToArray(); 
     var details = salesOrderDetailRepository.All.ToArray(); 
     var ages = ageRepository.All.ToArray(); 
     var custAges = customerAgeRepository.All.ToArray(); 
     var kinds = foodKindRepository.All.ToArray(); 
     var types = foodTypeRepository.All.ToArray(); 
     var units = unitOfMeasureRepository.All.ToArray(); 

     var products = from p in productRepository.All.ToArray() 
         select new { 
          productId = p.ProductId, 
          code = p.Name, 
          typeId = p.TypeId, 
          kindId = p.KindId, 
          Description = p.Description, 
          unitId = p.UnitId, 
          weight = (p == null) ? 0 : p.AverageWeight 
         }; 

     var productOrdered = from o in orders 
          join d in details on o.SalesOrderHeaderId equals d.SalesOrderId 
          join c in custAges on o.CustomerId equals c.CustomerId 
          join a in ages on c.AgeId equals a.AgeId 
          join p in products on d.ProductId equals p.productId 
          select new { 
           id = d.ProductId, 
           code = p.code, 
           ageId = a.AgeId, 
           quantity = (null == d) ? 0 : d.Quantity, 
           weight = ((null == d) ? 0 : d.Quantity) * ((null == p) ? 0 : p.weight), 
           deliveryDate = o.DeliveryDateTime 
          }; 

     var tomorrow = from p in productOrdered 
         where p.deliveryDate.Date == DateTime.Now.AddDays(1).Date 
         group p by new { p.id, p.ageId} into g 
         orderby g.Key.id 
         orderby g.Key.ageId 
         select new { 
          productId = g.Key.id, 
          ageId = g.Key.ageId, 
          quantity = g.Count(), 
          weight = g.Sum(s => s.weight) 
         }; 

     var now = from p in productOrdered 
        where p.deliveryDate.Date == DateTime.Now.Date 
        group p by new { p.id, p.ageId } into g 
        orderby g.Key.id 
        orderby g.Key.ageId 
        select new { 
         productId = g.Key.id, 
         ageId = g.Key.ageId, 
         quantity = g.Count(), 
         weight = g.Sum(s => s.weight) 
        }; 

     var yesterday = from p in productOrdered 
         where p.deliveryDate.Date == DateTime.Now.AddDays(-1).Date 
         group p by new { p.id, p.ageId } into g 
         orderby g.Key.id 
         orderby g.Key.ageId 
         select new { 
          productId = g.Key.id, 
          ageId = g.Key.ageId, 
          quantity = g.Count(), 
          weight = g.Sum(s => s.weight) 
         }; 

     var prods = from pr in products 
        join p in productOrdered on pr.productId equals p.id 
        join t in types on pr.typeId equals t.FoodTypeId 
        join k in kinds on pr.kindId equals k.FoodKindId 
        join u in units on pr.unitId equals u.AUnitMeasureId 
        select new { 
         productId = pr.productId, 
         ageId = p.ageId, 
         code = pr.code, 
         product = t.Name + " " + k.Name + " " + pr.Description, 
         uom = u.Name 
        }; 

     var grp = (from pr in prods 
        group new { pr.code, pr.product, pr.uom} by new { pr.productId, pr.ageId} into g 
        orderby g.Key.productId 
        orderby g.Key.ageId 
        select new { 
         productKey = g.Key, 
         productId = g.Key.productId, 
         ageId = g.Key.ageId, 
         code = g.Max(s => s.code), 
         product = g.Max(s => s.product), 
         uom = g.Max(s => s.uom) 
        }).Distinct(); 

     var model = from gr in grp 
        join y in yesterday on gr.productKey equals new { y.productId, y.ageId } into outer0 
        from y in outer0.DefaultIfEmpty() 
        join n in now on gr.productKey equals new { n.productId, n.ageId } into outer1 
        from n in outer1.DefaultIfEmpty() 
        join t in tomorrow on gr.productKey equals new { t.productId, t.ageId } into outer2 
        from t in outer2.DefaultIfEmpty() 
        select new RequiredProductsViewModel 
        { 
         ProductId = gr.productId, 
         Code = gr.code.ToString(), 
         Description = gr.product.ToString(), 
         AgeId = gr.ageId, 
         MinusQ = (!(null == y) ? y.quantity : 0), 
         MinusW = (!(null == y) ? decimal.Parse(y.weight.ToString()) : 0), 
         ZeroQ = (!(null == n) ? n.quantity : 0), 
         ZeroW = (!(null == n) ? decimal.Parse(n.weight.ToString()) : 0), 
         OneQ = (!(null == t) ? t.quantity : 0), 
         OneW = (!(null == t) ? decimal.Parse(t.weight.ToString()) : 0), 
         UofM = gr.uom.ToString() 
        }; 

     return View(new GridModel<RequiredProductsViewModel> 
     { 
      Data = model 
     }); 

最有可能會有其他(也許更好)的解決方案;然而,這是工作,這是我的故事,我堅持它。

最後感謝PanJanek花時間提出建議。請讓我知道,如果你找到任何方法來改善這一點。