2011-12-30 5 views
1

Наш первоначальный запрос состоит из множества подзапросов, которые работают правильно, на основе объединений, которые используют один столбец (productId). Полученная модель отображает сетку, в которой перечислены названия продуктов вместе с соответствующими требуемыми количествами для вчера, сегодня и завтра.LINQ Multiple Column Join

Однако требование было получено для дополнительного дифференциатора в зависимости от возраста продукта, и поэтому необходимо было изменить исходный запрос.

Таким образом, следующий код представляет собой модификацию рабочего кода, который использует одно поле, ProductId в качестве ключа. При попытке изменить запрос, чтобы использовать несколько ключ столбца (ProductID и возраст), я столкнулся с проблемой, получаем следующее сообщение об ошибке:

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

В предыдущем запросе, который создает Distinct Aggregate, я изменил ключ к композиту ProductId и возраст и назначить нового члена анонимного типа, productKey - new {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}

Поскольку оба типа «б в новый {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 смогут увидеть решение.

ответ

0

Try DECLARE с именем типа множественного ключа столбца вместо использования анонимной:

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:

заказ по п с классом ProductKey выдаст ошибку (LINQ не знает, как приказать множественный класс столбец), поэтому вы должны заказать g.Key.ProductId специально

+0

Когда я делаю вышеуказанные изменения, я получаю сообщение об ошибке «По крайней мере, один объект должен реализовать IComparable». Это отображается во всех вышеперечисленных запросах: «вчера», «grp» и «model», а также те запросы, которые не показаны («сейчас» и «завтра»), но такие же, как и запрос «вчера». –

+0

@ user946045: IComparable определяет метод CompareTo, который рассказывает, как сравнивать два экземпляра класса. Это необходимо для заказа, но в этом случае, я думаю, было бы лучше заказать конкретный столбец - обновлено aswer – PanJanek

+0

Ну, пока что создание класса productKey, похоже, делает 2 шага назад. Когда я использую анонимный тип для всех запросов, предшествующих последним, я получаю точные результаты на каждом шаге. Когда я использую определенный класс, я получаю IComparable ошибку, ни в коем случае я использую предложение Order By. –

0

Следующие изменения, как представляется, дали требуемый результат.

  1. В зависимых запросах группировки был изменен на новых {p.id, p.ageId}
  2. Всех OrderBy положений были пересмотрены с одной OrderBy на основе g.Key 2 отдельных OrderBy статей на основе g.Key.id и g.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 за то, что вы сделали предложения. Пожалуйста, дайте мне знать, если вы найдете какой-либо способ улучшить это.