Как использовать Любой между двумя

1

У меня есть следующий код внутри моего веб-приложения asp.net MVC:

var currentport = tms.TMSSwitchPorts
                     .Where(a => a.SwitchID == fromID)
                     .Select(a2 => a2.PortNumber)
                     .ToList();
            if (currentport.Any(tms.TMSSwitchPorts
                                   .Where(a => a.SwitchID == toID)
                                   .Select(a => a.PortNumber)
                                   .ToList()
                               )
               )
            {
              // do something
            }

но я не могу использовать .Any() таким образом, хотя я выбираю поле PortNumber в обоих списках?

Может ли кто-нибудь посоветовать?

Теги:
linq
entity-framework
asp.net-mvc-4

4 ответа

0
Лучший ответ

Если ваша цель как jwg описана в его комментарии, вы можете сделать:

var currentportFrom = tms.TMSSwitchPorts
                 .Where(a => a.SwitchID == fromID)
                 .Select(a2 => a2.PortNumber)
                 .ToList();
var currentportTo = tms.TMSSwitchPorts
                 .Where(a => a.SwitchID == fromID)
                 .Select(a2 => a2.PortNumber)
                 .ToList();

if(currentportFrom.Any(cp => currentportTo.Contains(cp))
{
    //do something
}
  • 1
    но таким образом я буду извлекать все записи из БД. Я пытался сделать Any () для двух Ienumerables, чтобы при возникновении любого конфликта остановить процессию ...
  • 0
    Это хорошее решение. Если вы действительно беспокоитесь об эффективности, вы должны сделать ToList для currentportTo , чтобы эти данные currentportTo только один раз и сохранялись в памяти, но не делать ToList для currentportFrom , чтобы Any() останавливался, как только конфликтующий элемент найден.
Показать ещё 7 комментариев
4

Вы можете это эффективно и полностью использовать в базе данных, используя простой Join:

var match = tms.TMSSwitchPorts.Where(a => a.SwitchID == fromID)
                              .Join(tms.TMSSwitchPorts.Where(a => a.SwitchID == toID),
                                    (a) => a.PortNumber,
                                    (b) => b.PortNumber,
                                    (a, b) => true).Any();

if (match) { ... }

Сгенерированный SQL должен выглядеть так:

SELECT 
    (CASE 
        WHEN EXISTS(
            SELECT NULL AS [EMPTY]
            FROM [TMSSwitchPorts] AS [t0]
            INNER JOIN [TMSSwitchPorts] AS [t1] ON [t0].[PortNumber] = [t1].[PortNumber]
            WHERE ([t0].[SwitchID] = @p0) AND ([t1].[SwitchID] = @p1)
            ) THEN 1
        ELSE 0
     END) AS [value]

поэтому у вас нет каких - либо данных ожидают один 1 или 0 движется по проводам, и вы не должны загромождать память приложения.


Код

if (currentport.Any(tms.TMSSwitchPorts
                               .Where(a => a.SwitchID == toID)
                               .Select(a => a.PortNumber)
                               .ToList()
                           )

не будет работать, потому что Any ожидает предикат в виде Func<T, bool>, но вы передаете ему List<Int> (предполагая, что PortNumber является int).

  • 0
    +1 Это лучшее решение, если tms - это запрос к базе данных, а не какой-либо другой источник. Не хочу редактировать, чтобы навязать мой стиль, но я бы хотел, чтобы вы переместили Any() в его собственную строку :)
  • 1
    Да, это не было ясно в вопросе, что это linq-to-sql (или -entities) или что минимизация передачи данных из базы данных была целью. Имея это в виду, это, вероятно, предпочтительное решение
Показать ещё 2 комментария
1

На основе комментария jwg вы можете проверить, есть ли совпадение между первым набором портов (From) и вторым набором (to), с использованием соответствия Contains:

var fromPorts = tms.TMSSwitchPorts
                 .Where(a => a.SwitchID == fromID)
                 .Select(a2 => a2.PortNumber);

if (tms.TMSSwitchPorts
       .Any(a => a.SwitchID == toID && 
                 fromPorts.Contains(a.PortNumber)))
1

Возьмите аналогичный список для SwitchID == toID.

var alreadyUsed = tms.TMSSwitchPorts
                     .Where(a => a.SwitchID == toID)
                     .Select(a2 => a2.PortNumber)
                     .ToList();

Затем просто проверьте, что ничего не отображается в обоих двух списках.

if (currentPort.Intersect(alreadyUsed).Any())
{ // do something }

Объяснение:

Any() не работает так, как вы думаете. Сам по себе, как используется выше, он проверяет любые элементы в контейнере. Как показано в ответе @BenAaronson (что немного лучше, чем этот ответ), он проверяет, содержит ли IEnumerable любой элемент, для которого аргумент функции возвращает true.

list1.Any(HasSomeProperty)

где HasSomeProperty это функция, которая принимает элемент list1 и возвращает логическое значение; или чаще всего с лямбдой:

list1.Any(x => SomePropertyHoldsFor(x))

Редактировать:

Я сказал, что ответ @BenAaronson был лучше, потому что он позволяет некоторые "короткие замыкания" оптимизаций, которые я не думал о моем решении. Они упомянуты в комментариях к его ответу. Однако с некоторыми проб и ошибок я обнаружил, что Intersect автоматически выполняет те же самые оптимизации - в частности, он кэширует "вывод" одного IEnumerable для сравнения с каждым элементом другого, а не пересекает его каждый раз. Таким образом, мое решение лучше, так как Intersect автоматически делает то, что вам нужно немного подумать о методе Бена;) @sloth ответ включает в себя другую автоматическую оптимизацию и гораздо лучше подходит для запроса базы данных с использованием Queryable который имел в виду айзер,

В этом ответе объясняется, как работает Intersect - на самом деле это не кеширование, а доступ к каждому IEnumerable только один раз.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню