Как применить оператор LINQ to SQL Distinct () к списку <T>? - PullRequest
6 голосов
/ 19 апреля 2011

У меня серьезная (это сводит меня с ума) проблема с LINQ to SQL .Я разрабатываю приложение ASP.NET MVC3 с использованием c # и Razor в Visual Studio 2010.

У меня есть две таблицы базы данных, Product и Категории :

Продукт (Prod_Id [первичный ключ], другие атрибуты)

Категории ((Dept_Id, Prod_Id) [первичные ключи], другие атрибуты)

Очевидно, Prod_Id в Категории - это внешний ключ.Оба класса отображаются с использованием Entity Framework (EF).Я не упоминаю контекст приложения для простоты.

В категориях есть несколько строк, содержащих Prod_Id .Я хочу сделать прогноз всех Distinct Prod_Id в категориях.Я сделал это с помощью обычного (T) SQL в SQL Server MGMT Studio в соответствии с этим (очень простым) запросом:

SELECT DISTINCT Prod_Id
FROM Categories

, и результат правильный.Теперь мне нужно сделать этот запрос в моем приложении, поэтому я использовал:

var query = _StoreDB.Categories.Select(m => m.Prod_Id).Distinct();

Я иду, чтобы проверить результат моего запроса с помощью:

query.Select(m => m.Prod_Id);

или

foreach(var item in query)
{
  item.Prod_Id;
  //other instructions
}

и это не работает.Прежде всего Intellisense, когда я пытаюсь написать query.Select(m => m. или item., показывает только предложения о методах (таких как Equals и т. Д.), А не свойства.Я подумал, что, возможно, что-то было не так с Intellisense (я думаю, что многие из вас много раз надеялись, что Intellisense был неправ :-D), но при запуске приложения я получаю сообщение об ошибке во время выполнения.

Прежде чем дать свой ответимейте в виду, что:

1) Я проверил много форумов, я попробовал нормальный LINQ to SQL (без использования лямбд), но он не работает.Тот факт, что он работает в (T) SQL, означает, что что-то не так с инструкцией LINQ to SQL (другие запросы в моем приложении работают отлично).

2) По причинам, связанным с приложением, я использовал List<T> переменная вместо _StoreDB.Categories, и я подумал, что это проблема.Если вы можете предложить мне решение без использования List<T>, также приветствуется.

Спасибо за ваши ответы

Francesco

Ответы [ 3 ]

7 голосов
/ 19 апреля 2011

Эта строка:

var query = _StoreDB.Categories.Select(m => m.Prod_Id).Distinct();

Ваш запрос LINQ, скорее всего, возвращает IEnumerable ... из ints (судя по Select(m => m.Prod_Id)). У вас есть список целых чисел, а не список объектов сущностей. Попробуйте напечатать их и посмотрите, что у вас есть.

4 голосов
/ 19 апреля 2011

Вызов _StoreDB.Categories.Select(m => m.Prod_Id) означает, что query будет содержать Prod_Id значения только , а не всю сущность.Это было бы примерно эквивалентно этому SQL, который выбирает только один столбец (вместо всей строки):

SELECT Prod_Id FROM Categories;

Так что, когда вы перебираете query, используя foreach (var item in query), тип itemвероятно int (или каков ваш столбец Prod_Id), не ваша сущность.Вот почему Intellisense не показывает свойства сущностей, которые вы ожидаете при вводе «item.» ...

Если вы хотите, чтобы все столбцы в Categories были включены в query, выдаже не нужно использовать .Select(m => m).Вы можете просто сделать это:

var query = _StoreDB.Categories.Distinct();

Обратите внимание, что если вы явно не передадите IEqualityComparer<T> в Distinct(), EqualityComparer<T>.Default будетиспользуемый (который может или не может вести себя так, как вы хотите, в зависимости от типа T, реализует ли он System.IEquatable<T> и т. д.).

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

0 голосов
/ 17 января 2013

Как было объяснено другими ответами, ошибка, с которой столкнулся OP, заключалась в том, что результатом его кода была коллекция целых чисел, а не коллекция категорий.

То, что не было ответа, было его вопросом о том, как использовать коллекцию целых чисел в соединении или что-то еще, чтобы получить некоторые полезные данные. Я попытаюсь сделать это здесь.

Теперь я не совсем уверен, почему ОП хотел получить отдельный список Prod_Ids из категорий, а не просто получить Prod_Ids из проектов. Возможно, он хотел выяснить, какие продукты относятся к одной или нескольким категориям, поэтому любые некатегоризованные продукты будут исключены из результатов. Я предполагаю, что это так, и что желаемый результат - это набор отдельных продуктов, которые имеют связанные категории. Сначала я отвечу на вопрос о том, что делать с Prod_Ids, а затем предложу несколько альтернатив.

Мы можем взять коллекцию Prod_Ids точно так, как они были созданы в вопросе как запрос:

var query = _StoreDB.Categories.Select(m => m.Prod_Id).Distinct();

Тогда мы будем использовать объединение, вот так:

var products = query.Join(_StoreDB.Products, id => id, p => p.Prod_Id,
               (id,p) => p);

Принимает запрос, объединяет его с таблицей Products, указывает используемые ключи и, наконец, говорит, что нужно возвращать сущность Product из каждого соответствующего набора. Поскольку мы знаем, что Prod_Ids в запросе уникальны (из-за Distinct ()), а Prod_Ids в продуктах уникальны (по определению, потому что это первичный ключ), мы знаем, что результаты будут уникальными без вызова Distinct (). .

Теперь все вышесказанное даст желаемые результаты, но это определенно не самый чистый или самый простой способ сделать это. Если сущности Category определены с помощью реляционного свойства, которое возвращает связанную запись из Products (которая, вероятно, будет называться Product), простейшим способом сделать то, что мы пытаемся сделать, будет следующее:

var products = _StoreDB.Categories.Select(c => c.Product).Distinct();

Это получает Продукт из каждой Категории и возвращает отдельную коллекцию из них. Если у сущности Category нет реляционного свойства Product, мы можем вернуться к использованию функции Join для получения наших Продуктов.

var products = _StoreDB.Categories.Join(_StoreDB.Products, c => c.Prod_Id,
               p => p.Prod_Id, (c,p) => p).Distinct();

Наконец, если мы не просто хотим получить простую коллекцию Продуктов, то нужно еще кое-что рассмотреть, и, возможно, самой простой вещью будет обращение с этим при переборе Продуктов. Другой пример - подсчет количества категорий, к которым принадлежит каждый продукт. Если это так, я бы изменил логику и начал с продуктов, например так:

var productsWithCount = _StoreDB.Products.Select(p => new { Product = p,
    NumberOfCategories = _StoreDB.Categories.Count(c => c.Prod_Id == p.Prod_Id)});

Это приведет к коллекции анонимных типизированных объектов, которые ссылаются на Product и NumberOfCategories, связанные с этим Product. Если нам все еще нужно исключить некатеризованные продукты, мы можем добавить .Where(r => r.NumberOfCategories > 0) перед точкой с запятой. Конечно, если сущность Product определена с помощью реляционного свойства для связанных категорий, вам это не понадобится, поскольку вы можете просто взять любой продукт и выполнить следующие действия:

int NumberOfCategories = product.Categories.Count();

Во всяком случае, извините за то, что мы пошли дальше. Я надеюсь, что это окажется полезным для всех, кто сталкивается с подобной проблемой. ;)

...