Відповіді:
Вам потрібно повернути його на голову з точки зору того, як ви про це думаєте. Замість того, щоб робити "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(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))
Якщо ви використовуєте VS2008 / .net 3.5, див. Підказку Алекса Джеймса №8: http://blogs.msdn.com/alexj/archive/2009/03/26/tip-8-writing-where-in-style -запити-використання-linq-to-elements.aspx
В іншому випадку просто використовуйте метод array.Contains (someEntity.Member).
У цьому контексті я піду за Внутрішнє приєднання. Якби я використовував вміст, він повторився б 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
Використовуючи Містить, він шукатиме кожен елемент у списку 1 у списку 2, що означає, що повторення відбудеться 49 разів !!!
Я також намагався працювати з подібною SQL-IN річчю - запит проти моделі даних Entity . Мій підхід - це конструктор струн, щоб створити велике вираження АБО. Це жахливо потворно, але я боюся, що це єдиний шлях зараз.
Тепер добре, це виглядає приблизно так:
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 знав, як створити тестове середовище, де показано, чому його рішення погано.
where new[] { 1, 2, 3 }.Contains(x)
порівняння менше where (x == 1 || x == 2 || x == 3)
?