2013-08-19 4 views
3

Я знаком с простой операцией GROUP BY в LINQ. Но у меня сложный сценарий. Мне нужно преобразовать записи CostPageRow в объекты домена CostPageSelection, используя группу. Как мы можем получить объекты домена из списка?Комплекс GROUP BY с использованием LINQ

Примечание: Группа по тому, что я написал выше, не дает List. Я не уверен, как это написать. В списке CostPageSelection есть список внутри него.

List<CostPageRow> costPageRows = searchDAL 
    .GetAllCostPages(contextObject, searchCriteria); 


var orderGroups = costPageRows 
    .GroupBy(x => new { 
     x.CostPage, 
     x.Description, 
     x.BillTypeDirect, 
     x.BillTypeWarehouse, 
     x.OrderType, 
     x.Vendor, 
     x.VendorID 
    }) 
    .Select(y => new CostPageParent { 
      CostPage = y.First().CostPage 
    }) 
    .ToList(); 

Результат от хранимой процедуры

public class CostPageRow 
{ 
    //CostPage Parent 

    public string CostPage { get; set; } 
    public string Description { get; set; } 
    public string Vendor { get; set; } 
    public string VendorID { get; set; } 
    public string BillTypeDirect { get; set; } 
    public string BillTypeWarehouse { get; set; } 
    public string OrderType { get; set; } 

    //Item Chilld 
    public string ItemID { get; set; } 
    public string ItemDescription { get; set; } 
    public string BrandCode { get; set; } 
    public string PackSize { get; set; } 
} 

домена Модель

public class CostPageSelection 
{ 
    public CostPageParent CostPageParent { get; set; } 
    public List<ItemChild> ChildItems { get; set; } 
} 


//CostPageParent 
public class CostPageParent 
{ 
    public int? SelectedCostPageID { get; set; } 
    public string CostPage { get; set; } 
    public string Description { get; set; } 
    public string Vendor { get; set; } 
    public string VendorID { get; set; } 
    public string BillTypeDirect { get; set; } 
    public string BillTypeWarehouse { get; set; } 
    public string OrderType { get; set; } 
} 

//ItemChild 
public class ItemChild 
{ 
    public int? SelectedItemID { get; set; } 
    public string ItemID { get; set; } 
    public string ItemDescription { get; set; } 
    public string BrandCode { get; set; } 
    public string PackSize { get; set; } 
} 
+2

Итак, в чем проблема? –

+0

@lazyberezovsky. Группа по тому, что я написал выше, не дает List . Я не уверен, как это написать. В списке CostPageSelection есть список внутри него. – Lijo

+0

Значок Использования Выбрать, чтобы найти: –

ответ

4

Из вашего вопроса, то появляется что ваша база данных продолжается ains сглаженная иерархия ваших отношений родитель-потомок, и было бы намного лучше, если бы схема базы данных была нормализована, чтобы избежать дублирования данных: другими словами, каждая строка CostPageRow должна содержать ссылку на другую таблицу, содержащую соответствующий пример CostPageParent (но я предполагаю, что у вас уже есть работающая база данных, и это не вариант).

Для решения текущей проблемы, как это, вам нужно извлечь свойства, которые определяют одну группу из каждого CostPageRow экземпляра (эти свойства будут формировать новый CostPageParent экземпляр), а затем создать группы, используя эти CostPageParent экземпляры в качестве уникальных ключей , и, наконец, проецируем группы на новые экземпляры CostPageSelection (каждый с уникальным ключом CostPageParent).

Вы должны изменить код, чтобы использовать IGrouping<T>.Key свойство, чтобы получить ключ группы, после ее создания:

var groups = costPageRows 
    .GroupBy(x => new CostPageParent() 
     { 
      CostPage = x.CostPage, 
      Description = x.Description, 
      BillTypeDirect = x.BillTypeDirect, 
      BillTypeWarehouse = x.BillTypeWarehouse, 
      OrderType = x.OrderType, 
      Vendor = x.Vendor 
     }, 
     new CostPageParentEqualityComparer()) 
    .Select(y => new CostPageSelection 
     { 
      CostPageParent = y.Key, 
      ChildItems = y.Select(i => 
       new ItemChild() 
       { 
        BrandCode = i.BrandCode, 
        ItemDescription = i.ItemDescription, 
        ItemID = i.ItemID, 
        PackSize = i.PackSize 
       }) 
       .ToList() 
     }) 
    .ToList(); 

Обратите внимание, что вам необходимо указать IEqualityComparer<CostPageParent> реализации, чтобы сделать группировку работы свойством:

class CostPageParentEqualityComparer : IEqualityComparer<CostPageParent> 
{ 
    public bool Equals(CostPageParent x, CostPageParent y) 
    { 
     if (x == null) 
      return y == null; 

     if (object.ReferenceEquals(x, y)) 
      return true; 

     return 
      x.BillTypeDirect == y.BillTypeDirect && 
      x.BillTypeWarehouse == y.BillTypeWarehouse && 
      ... 
    } 

    public int GetHashCode(CostPageParent obj) 
    { 
     var x = 31; 
     x = x * 17 + obj.BillTypeDirect.GetHashCode(); 
     x = x * 17 + obj.BillTypeWarehouse.GetHashCode(); 
     ... 
     return x; 
    } 
} 
+0

Спасибо .. Это работает. Не могли бы вы объяснить, почему нам нужна логика GetHashCode? Группирование работает нормально, даже если я возвращаю жестко закодированные значения в результате GetHashCode. – Lijo

+1

@Groo 'GroupBy' реализуется, внутренне, используя структуру данных на основе хэша (« Словарь »или аналогичный), поэтому вы, безусловно, ** должны ** иметь правильную реализацию типа GetHashCode, если хотите для использования на нем 'GroupBy'. – Servy

+1

@Lijo Это работает, потому что правила правильной реализации 'GetHashCode' говорят, что все объекты, равные в соответствии с' Equals' *, должны * иметь один и тот же хэш-код. Лучше всего обеспечить, чтобы неравные объекты имели меньше шансов иметь один и тот же хеш (некоторые из них неизбежны). Чем больше объектов разделяет хеш, тем хуже будет работать структура данных. Если все объекты имеют одинаковый код, он будет * работать *, но будет очень медленным. См. [Эту статью] (http://blogs.msdn.com/b/ericlippert/archive/2011/02/28/guidelines-and-rules-for-gethashcode.aspx) для получения дополнительной информации. – Servy

3

Я думаю, что вы ищете что-то вроде этого:

var orderGroups = costPageRows 
.GroupBy(x => new { 
    x.CostPage, 
    x.Description, 
    x.BillTypeDirect, 
    x.BillTypeWarehouse, 
    x.OrderType, 
    x.Vendor, 
    x.VendorID 
}) 
// The following statement is basically a let statement from query expression 
.Select(y => new { 
     y, 
     first = y.First() 
}) 
// What happens next is projection into the CostPageSelection type using z.first 
// and then all the grouped items in z are projected into the ItemChild type 
.Select(z => new CostPageSelection { 
     new CostPageParent { 
      z.first.CostPage, 
      // other props 
      ChildItems = z.Select(i => new ItemChild { 
       i.ItemID, 
       // other props 
      }).ToList() 
}) 
.ToList();