2011-12-30 45 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花时间提出建议。请让我知道,如果你找到任何方法来改善这一点。