Я хочу знать, как я могу группировать строки в своей таблице с помощью LINQ. Приведенный ниже код успешно загрузил мою сетку с данными из базы данных, поэтому мне нужно выбрать и сгруппировать их в хранилище и элемент.
[HttpGet]
public object Get()
{
return new { Items = _context.farmtable
.Select(x => new {x.Id, x.Item, x.Store, x.Location })
.ToList(), Count = _context.farmtable.Count() };
}
Это то, что я пробовал, но ничего не отображается.
[HttpGet]
public object Get()
{
return new { Items = _context.farmtable.GroupBy(x => x.Store, x => x.Item )
.Select(group => new { Store = group.Key, Item = group.Key, Items = group.ToList() })
.ToList(), Count = _context.farmtable.Count() };
}
Ожидаемый результат
Вам следует перестать пересматривать то, что делает этот код, потому что он полон проблем и противоречий. Предполагая, что _context
— это DbContext, вы имеете дело не с таблицами, а с сущностями приложения. Сеток нет. Это Get
возвращает список объектов, НО стирает типы. Все API используют для результатов конкретные типы, а не анонимные типы. Этот метод все равно ничего не отображает, он возвращает JSON. Хуже того, сам запрос EF является частью анонимного типа, поэтому вы даже не можете увидеть, какая часть запроса и куда его поместить GroupBy
.
Что ты вообще пытаешься вернуть? Ожидаемый результат — это просто уникальные комбинации Store и Item без подсчетов или чего-то еще. Запрос пытается вернуть все отдельные элементы в корзинах магазина. Это ничем не отличается от загрузки всех элементов в память и их последующей группировки. Он не может использовать GROUP BY
в SQL.
Как насчет farmtable.GroupBy(item => (item.Store, item.Item)).Select(group => (group.Key, group.Count())
. Это должно работать с IEnumerable. Если вы имеете дело с базой данных и это IQueryable, то это зависит от вашего поставщика базы данных.
_context.Farmtable .Select(x => new { Store = x.Store, Item = x.Item }) .GroupBy(g => new { g.Store, g.Item }) .Select(s => new { s .Key.Store, s.Key.Item, count = s.Count()})
[HttpGet]
public object Get()
{
return _context.Farmtable.AsNoTracking()
.Select(x => new { Store = x.Store, Item = x.Item })
.GroupBy(g => new { g.Store, g.Item })
.Select(s => new { Store = s.Key.Store, Item = s.Key.Item, count = s.Count() });
}
По соображениям эффективности при объединении операторов LINQ следует сохранять промежуточные результаты IEnumerable<...>
(или IQueryable<...>
) как можно дольше. Поэтому не используйте ToList
или ToArray
, если вы не собираетесь использовать результаты.
Причина этого в том, что если вызывающему абоненту нужны не все элементы, а только первые несколько, то вы используете много вычислительной мощности для вещей, которые не используются.
Например, если звонящий на ваш Get
хочет только узнать, есть ли какой-нибудь предмет, который можно получить:
public bool AreFarmsAvailabe()
{
return this.Get().Any();
}
Если метод Get
использует ToList
, то в список помещаются тысячи ферм, и единственное, что при этом делается, — это проверка, есть ли что-нибудь в списке. Так что вы могли бы остановиться, как только у вас будет одна ферма.
Поэтому рассмотрите возможность изменения метода Get
class FarmStoreLocation // TODO: invent a proper name
{
public int Id {get; set;}
public string Item {get; set;} // might be an enum, or a class
public string Store {get; set;}
public string Location {get; set;}
}
И ваш метод Get:
IEnumerable<FarmStoreLocation> GetFarmStoreLocations()
{
IEnumerable<FarmStoreLocation> fetchedFarmStoreLocations = _context.FarmTable
.Select(farm => new FarmStoreLocation
{
Id = farm.Id,
Item = farm.Item,
Store = farm.Store,
Location = farm.Location,
});
return fetchedFarmStoreLocations;
}
Я не знаю, возвращает ли ваш _context.FarmTable IEnumerable<...>
или IQueryable<...>
, если последнее, то пусть процедура возвращает IQueryable.
Преимущество изменения метода Get заключается в том, что гораздо проще проверить, возвращает ли метод то, что вы хотите. Вы можете выполнить модульное тестирование. Вы можете повторно использовать его в других запросах, и если позже входные данные изменятся, например, если позже вы получите данные из базы данных или из файла CSV, пользователям вашего метода Get не придется меняться.
Требование: Учитывая входную последовательность FarmStoreLocations, преобразуйте ее в последовательность комбинаций [Магазин, Товар] и подсчитайте, сколько этих комбинаций находится во входной последовательности.
Итак, вы хотите посчитать, сколько раз вы видите комбинацию [Дом А, Яблоко] и сколько раз вы видите комбинацию [Дом А, Банан] и т. д.
Как вы уже поняли, для этого вам понадобится одна из перегрузок Enumerable.GroupBy. Вы хотите создать группы FarmStoreLocations с одинаковым значением для [Store, Item]. После этого вам нужно подсчитать количество FarmStoreLocations в каждой группе. Для этого я использую перегрузку Enumerable.GroupBy, имеющую оба параметра KeySelector и ResultSelector:
IEnumerable<FarmStoreLocation> farmStoreLocations = GetFarmStoreLocations();
var countedStoreItems = farmStoreLocations.GroupBy (
// parameter keySelector: make groups of farmStoreLocations that have the same
// value for [Store, Item]
farmStoreLocation => new
{
Store = farmStoreLocation.Store,
Item = farmStoreLocation.Item,
},
// parameter resultSelector: from every found combination of [Store, Item]
// and all farmStoreLocations that have these values for Store and Item
// make one new:
(storeItemCombination, farmStoreLocationsWithThisStoreItemCombination) => new
{
Store = storeItemCombination.Store,
Item = storeItemCombination.Item,
Count = farmStoreLocationsWithThisStoreItemCombination.Count(),
});
Другими словами: из входной последовательности FarmStoreLocations создайте группы FarmStoreLocations, которые имеют одинаковое значение свойств Store и Item. Ключом группы является объект-комбинация [Магазин, Товар].
Результатом является последовательность групп, где каждая группа будет иметь ключ, который представляет собой [Store, Item], и все FarmStoreLocations, которые имеют эти значения для Store и Item.
Из каждой группы возьмите ключ (= одну комбинацию storeItemCombination) и всех членов группы (= все фермыStoreLocations с этой комбинацией storeItemCombination) и создайте для каждой группы ровно один объект.
Этот один объект содержит значение Store, которое есть у всех FarmStoreLocations группы, и значение Item, которое есть у всех FarmStoreLocations группы. Наконец, этот объект содержит количество FarmStoreLocations в группе.
Какой у Вас вопрос? У вас возникла ошибка или что-то в этом выводе не соответствует вашим ожиданиям? Лучше быть откровенным, чем предполагать, что мы догадаемся правильно!