Предложение NOT IN в LINQ to Entities

Есть ли способ создать предложение not in, как это было бы в SQL Server в Linq to Entities?


person dagda1    schedule 11.01.2009    source источник


Ответы (5)


Если вы используете коллекцию в памяти в качестве фильтра, вероятно, лучше всего использовать отрицание Contains (). Обратите внимание, что это может не сработать, если список слишком длинный, и в этом случае вам нужно будет выбрать другую стратегию (см. Ниже для использования стратегии для полностью ориентированного на БД запроса).

   var exceptionList = new List<string> { "exception1", "exception2" };

   var query = myEntities.MyEntity
                         .Select(e => e.Name)
                         .Where(e => !exceptionList.Contains(e.Name));

Если вы исключаете на основе другого запроса к базе данных, использование Except может быть лучшим выбором. (Вот ссылка на поддерживаемые расширения Set в LINQ to Entities)

   var exceptionList = myEntities.MyOtherEntity
                                 .Select(e => e.Name);

   var query = myEntities.MyEntity
                         .Select(e => e.Name)
                         .Except(exceptionList);

Это предполагает сложную сущность, в которой вы исключаете определенные в зависимости от некоторого свойства другой таблицы и хотите, чтобы имена сущностей не исключались. Если вам нужна вся сущность, вам нужно будет создать исключения как экземпляры класса сущности, чтобы они удовлетворяли оператору равенства по умолчанию (см. docs).

person tvanfosson    schedule 11.01.2009
comment
По какой-то причине Except производит ужасный SQL. Contains - это метод, который следует использовать здесь: myEntities.MyEntity.Select(e => e.Name ).Where(x => !exceptionList.Contains(x)). - person Gert Arnold; 10.08.2013
comment
@GertArnold, не могли бы вы уточнить, что оператор производит ужасный SQL? Я использую Except, и он работает нормально. Ни странностей, ни причуд в работе, AFAIK. - person NinjaCross; 06.10.2014
comment
@NinjaCross Оператор, как в ответе выше, производит SQL с n-1 UNION ALL предложениями, где n - количество элементов в exceptionList. Я просто пробовал с EF 6.1, так что дело не в том, что он улучшился или что-то в этом роде. В EF 4.1 то же самое, поэтому я просто не понимаю, почему этот ответ вообще был принят. Ответ, предлагающий Contains, правильный. Я думаю, вы использовали Except с другим IQueryable, поэтому EF смогла перевести его в SQL EXCEPT. А ты? - person Gert Arnold; 06.10.2014
comment
@GertArnold - not Contains вполне допустимо в пределах ограничения, согласно которому предложение NOT IN ограничивается примерно 2000 записями. После этого вы можете получить ошибку ресурсов SQL. Вероятно, это то, что я бы использовал в простом случае. - person tvanfosson; 06.10.2014
comment
@GertArnold, вот что я делаю: (из e в dataModel.TABLE1 выберите e.ID) .Except (dataModel.TABLE2.Select (m = ›m.LightTypeID)). Min (); Это сгенерированный запрос: SELECT [GroupBy1]. [A1] AS [C1] FROM (SELECT MIN ([Except1]. [ID]) AS [A1] FROM (SELECT [Extent1]. [ID] AS [ID] FROM [dbo]. [TABLE1] AS [Extent1] EXCEPT SELECT [Extent2]. [LightTypeID] AS [LightTypeID] FROM [dbo]. [TABLE2] AS [Extent2]) AS [Except1]) AS [GroupBy1] - person NinjaCross; 06.10.2014
comment
@NinjaCross Действительно, это два IQueryable с Except. Таким образом, все выражение содержит отображенные объекты и может быть переведено в SQL EXCEPT. Использование Except со списком в памяти отличается. @tvanfosson Я знаю, что есть какое-то исправление: stackoverflow.com/q/24534217/861716. Except (со списком в памяти) будет вызывать слишком глубокую вложенность задолго до этих чисел. Можем ли мы сделать вывод, что Except в порядке, пока задействованы только отображенные объекты, а в противном случае Contains лучше? - person Gert Arnold; 06.10.2014
comment
@GertArnold Для примера в этом ответе да. В качестве общего ответа я думаю, что с EF, вероятно, лучше сказать, что это зависит от обстоятельств - проверьте свою производительность, выберите стратегию, которая лучше всего работает. - person tvanfosson; 06.10.2014
comment
@GertArnold Я обновил ответ, чтобы устранить различные различия. В то время были другие ответы на этот случай, и я не хотел затрагивать ту же тему. Теперь, когда это старый и принятый ответ, я включил этот материал. - person tvanfosson; 06.10.2014

Пытаться:

from p in db.Products
where !theBadCategories.Contains(p.Category)
select p;

Какой SQL-запрос вы хотите преобразовать в Linq-запрос?

person yfeldblum    schedule 11.01.2009

У меня есть следующие методы расширения:

    public static bool IsIn<T>(this T keyObject, params T[] collection)
    {
        return collection.Contains(keyObject);
    }

    public static bool IsIn<T>(this T keyObject, IEnumerable<T> collection)
    {
        return collection.Contains(keyObject);
    }

    public static bool IsNotIn<T>(this T keyObject, params T[] collection)
    {
        return keyObject.IsIn(collection) == false;
    }

    public static bool IsNotIn<T>(this T keyObject, IEnumerable<T> collection)
    {
        return keyObject.IsIn(collection) == false;
    }

Использование:

var inclusionList = new List<string> { "inclusion1", "inclusion2" };
var query = myEntities.MyEntity
                     .Select(e => e.Name)
                     .Where(e => e.IsIn(inclusionList));

var exceptionList = new List<string> { "exception1", "exception2" };
var query = myEntities.MyEntity
                     .Select(e => e.Name)
                     .Where(e => e.IsNotIn(exceptionList));

Также очень полезно при прямой передаче значений:

var query = myEntities.MyEntity
                     .Select(e => e.Name)
                     .Where(e => e.IsIn("inclusion1", "inclusion2"));

var query = myEntities.MyEntity
                     .Select(e => e.Name)
                     .Where(e => e.IsNotIn("exception1", "exception2"));
person JoanComasFdz    schedule 02.03.2016
comment
Это полезно, но не может быть переведено на выражение магазина. - person Marek Bar; 26.04.2017
comment
Верно, но я бы хотел выполнить в базе данных. AsEnumerable загрузит данные в память. - person Marek Bar; 26.04.2017

Я взял список и использовал,

!MyList.Contains(table.columb.tostring())

Примечание. Убедитесь, что вы используете List, а не Ilist.

person Mayank    schedule 08.09.2011

Я создал его более похожим на SQL, думаю, это легче понять

var list = (from a in listA.AsEnumerable()
            join b in listB.AsEnumerable() on a.id equals b.id into ab
            from c in ab.DefaultIfEmpty()
            where c != null
            select new { id = c.id, name = c.nome }).ToList();
person Rodolfo Gaspar    schedule 21.11.2016