В T-SQL у вас может быть такой запрос:
SELECT * FROM Users WHERE User_Rights IN ("Admin", "User", "Limited")
Как бы вы воспроизвели это в запросе LINQ to Entities? Это вообще возможно?
В T-SQL у вас может быть такой запрос:
SELECT * FROM Users WHERE User_Rights IN ("Admin", "User", "Limited")
Как бы вы воспроизвели это в запросе LINQ to Entities? Это вообще возможно?
Вам нужно перевернуть его с ног на голову с точки зрения того, как вы думаете об этом. Вместо того, чтобы выполнять «in», чтобы найти права пользователя текущего элемента в предопределенном наборе применимых прав пользователя, вы запрашиваете предопределенный набор прав пользователя, содержит ли он применимое значение текущего элемента. Точно так же вы найдете элемент в обычном списке в .NET.
Есть два способа сделать это с помощью LINQ: один использует синтаксис запроса, а другой использует синтаксис метода. По сути, они одинаковы и могут использоваться взаимозаменяемо в зависимости от ваших предпочтений:
Синтаксис запроса:
var selected = from u in users
where new[] { "Admin", "User", "Limited" }.Contains(u.User_Rights)
select u
foreach(user u in selected)
{
//Do your stuff on each selected user;
}
Синтаксис метода:
var selected = users.Where(u => new[] { "Admin", "User", "Limited" }.Contains(u.User_Rights));
foreach(user u in selected)
{
//Do stuff on each selected user;
}
Моим личным предпочтением в этом случае может быть синтаксис метода, потому что вместо назначения переменной я мог бы выполнить foreach с помощью анонимного вызова следующим образом:
foreach(User u in users.Where(u => new [] { "Admin", "User", "Limited" }.Contains(u.User_Rights)))
{
//Do stuff on each selected user;
}
Синтаксически это выглядит более сложным, и вы должны понимать концепцию лямбда-выражений или делегатов, чтобы действительно понять, что происходит, но, как вы можете видеть, это изрядно сжимает код.
Все сводится к вашему стилю кодирования и предпочтениям - все три моих примера делают одно и то же немного по-разному.
Альтернативный способ даже не использует LINQ, вы можете использовать тот же синтаксис метода, заменяя «where» на «FindAll», и получить тот же результат, который также будет работать в .NET 2.0:
foreach(User u in users.FindAll(u => new [] { "Admin", "User", "Limited" }.Contains(u.User_Rights)))
{
//Do stuff on each selected user;
}
Этого должно хватить для вашей цели. Он сравнивает две коллекции и проверяет, имеет ли одна коллекция значения, соответствующие значениям в другой коллекции.
fea_Features.Where(s => selectedFeatures.Contains(s.feaId))
В этом контексте я выберу Inner Join. Если бы я использовал contains, он бы повторил 6 раз, несмотря на то, что есть только одно совпадение.
var desiredNames = new[] { "Pankaj", "Garg" };
var people = new[]
{
new { FirstName="Pankaj", Surname="Garg" },
new { FirstName="Marc", Surname="Gravell" },
new { FirstName="Jeff", Surname="Atwood" }
};
var records = (from p in people join filtered in desiredNames on p.FirstName equals filtered select p.FirstName).ToList();
Предположим, у меня есть два объекта списка.
List 1 List 2
1 12
2 7
3 8
4 98
5 9
6 10
7 6
Используя Contains, он будет искать каждый элемент списка 1 в списке 2, что означает, что итерация будет выполняться 49 раз !!!
Это может быть возможным способом, которым вы можете напрямую использовать методы расширения LINQ для проверки предложения in
var result = _db.Companies.Where(c => _db.CurrentSessionVariableDetails.Select(s => s.CompanyId).Contains(c.Id)).ToList();
Я также пробовал работать с SQL-IN-подобной вещью - запросами к модели данных сущности. Мой подход - построитель строк для составления большого ИЛИ-выражения. Это ужасно уродливо, но, боюсь, сейчас это единственный выход.
Что ж, это выглядит так:
Queue<Guid> productIds = new Queue<Guid>(Products.Select(p => p.Key));
if(productIds.Count > 0)
{
StringBuilder sb = new StringBuilder();
sb.AppendFormat("{0}.ProductId = Guid\'{1}\'", entities.Products.Name, productIds.Dequeue());
while(productIds.Count > 0)
{
sb.AppendFormat(" OR {0}.ProductId = Guid\'{1}\'",
entities.Products.Name, productIds.Dequeue());
}
}
Работа с идентификаторами GUID в этом контексте: как вы можете видеть выше, во фрагментах строки запроса всегда есть слово «GUID» перед самим идентификатором GUID. Если вы не добавите это, ObjectQuery<T>.Where
вызовет следующее исключение:
Типы аргументов Edm.Guid и Edm.String несовместимы для этой операции., Выражение почти равно, строка 6, столбец 14.
Нашел это на форумах MSDN, может быть полезно иметь в виду.
Матиас
... с нетерпением жду следующей версии .NET и Entity Framework, когда все наладится. :)
Альтернативный метод ответа BenAlabaster
Прежде всего, вы можете переписать запрос так:
var matches = from Users in people
where Users.User_Rights == "Admin" ||
Users.User_Rights == "Users" ||
Users.User_Rights == "Limited"
select Users;
Конечно, это более «многословно» и неудобно писать, но все равно работает.
Так что, если бы у нас был какой-то служебный метод, который упростил бы создание такого рода выражений LINQ, мы были бы в бизнесе.
используя служебный метод, вы можете написать что-то вроде этого:
var matches = ctx.People.Where(
BuildOrExpression<People, string>(
p => p.User_Rights, names
)
);
Это создает выражение, которое имеет тот же эффект, что и:
var matches = from p in ctx.People
where names.Contains(p.User_Rights)
select p;
Но что более важно, на самом деле работает против .NET 3.5 SP1.
Вот функция сантехники, которая делает это возможным:
public static Expression<Func<TElement, bool>> BuildOrExpression<TElement, TValue>(
Expression<Func<TElement, TValue>> valueSelector,
IEnumerable<TValue> values
)
{
if (null == valueSelector)
throw new ArgumentNullException("valueSelector");
if (null == values)
throw new ArgumentNullException("values");
ParameterExpression p = valueSelector.Parameters.Single();
if (!values.Any())
return e => false;
var equals = values.Select(value =>
(Expression)Expression.Equal(
valueSelector.Body,
Expression.Constant(
value,
typeof(TValue)
)
)
);
var body = equals.Aggregate<Expression>(
(accumulate, equal) => Expression.Or(accumulate, equal)
);
return Expression.Lambda<Func<TElement, bool>>(body, p);
}
Я не буду пытаться объяснять этот метод, кроме как сказать, что он, по сути, строит выражение предиката для всех значений, используя valueSelector (т.е. p => p.User_Rights) и объединяет эти предикаты вместе, чтобы создать выражение для полного предикат
Реальный пример:
var trackList = Model.TrackingHistory.GroupBy(x => x.ShipmentStatusId).Select(x => x.Last()).Reverse();
List<int> done_step1 = new List<int>() {2,3,4,5,6,7,8,9,10,11,14,18,21,22,23,24,25,26 };
bool isExists = trackList.Where(x => done_step1.Contains(x.ShipmentStatusId.Value)).FirstOrDefault() != null;
Шутки в сторону? Вы, ребята, никогда не использовали
where (t.MyTableId == 1 || t.MyTableId == 2 || t.MyTableId == 3)
Checks = NumValues * NumRows
. Поскольку это вычисление типа M * N, если какое-либо из них мало, то время на выполнение каждой требуемой проверки также будет небольшим. Я добавил ограничение, чтобы cjm30305 знал, как настроить тестовую среду, в которой показано, почему его решение неудовлетворительное.
- person Trisped; 18.09.2016
where new[] { 1, 2, 3 }.Contains(x)
делает меньше сравнений, чем where (x == 1 || x == 2 || x == 3)
?
- person tymtam; 20.09.2016
||
vs Contains
одинакова независимо от того, сколько ||
используется, а в MS SQL разница в производительности настолько велика (на 30% медленнее?). У меня такое ощущение, что ваши рассуждения о 1000, а то и о 100000 строках не соответствуют действительности. Не могли бы вы его поддержать?
- person tymtam; 21.09.2016