Odpowiedzi:
Musisz obrócić go na głowie pod względem sposobu, w jaki myślisz o tym. Zamiast robić „in”, aby znaleźć prawa użytkownika dla bieżącego elementu w predefiniowanym zestawie odpowiednich praw użytkownika, pytasz o predefiniowany zestaw praw użytkownika, jeśli zawiera on odpowiednią wartość dla bieżącego elementu. W ten sam sposób można znaleźć element na zwykłej liście w .NET.
Można to zrobić na dwa sposoby za pomocą LINQ, jeden używa składni zapytania, a drugi - metody. Zasadniczo są one takie same i mogą być używane zamiennie w zależności od preferencji:
Składnia zapytania:
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;
}
Składnia metody:
var selected = users.Where(u => new[] { "Admin", "User", "Limited" }.Contains(u.User_Rights));
foreach(user u in selected)
{
//Do stuff on each selected user;
}
Moją osobistą preferencją w tym przypadku może być składnia metody, ponieważ zamiast przypisywać zmienną, mógłbym wykonać foreach za pomocą anonimowego wywołania, takiego jak to:
foreach(User u in users.Where(u => new [] { "Admin", "User", "Limited" }.Contains(u.User_Rights)))
{
//Do stuff on each selected user;
}
Składniowo wygląda to na bardziej złożone i musisz zrozumieć pojęcie wyrażeń lambda lub delegatów, aby naprawdę zorientować się, co się dzieje, ale jak widać, skraca to sporo kod.
Wszystko sprowadza się do twojego stylu kodowania i preferencji - wszystkie trzy moje przykłady robią to samo nieco inaczej.
Alternatywny sposób nawet nie korzysta z LINQ, możesz użyć tej samej metody, zastępując „gdzie” słowem „FindAll” i uzyskać ten sam wynik, który będzie również działał w .NET 2.0:
foreach(User u in users.FindAll(u => new [] { "Admin", "User", "Limited" }.Contains(u.User_Rights)))
{
//Do stuff on each selected user;
}
To powinno wystarczyć do twojego celu. Porównuje dwie kolekcje i sprawdza, czy jedna kolekcja ma wartości pasujące do wartości w drugiej kolekcji
fea_Features.Where(s => selectedFeatures.Contains(s.feaId))
Jeśli używasz VS2008 / .net 3.5, zobacz wskazówkę Alexa Jamesa nr 8: http://blogs.msdn.com/alexj/archive/2009/03/26/tip-8-writing-where-in-style -queries-using-linq-to-entity.aspx
W przeciwnym razie wystarczy użyć metody array.Contains (someEntity.Member).
W tym kontekście wybiorę opcję dołączenia wewnętrznego. Gdybym użył zawierał, iterowałby 6 razy, mimo że fakt, że jest tylko jedno dopasowanie.
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();
Załóżmy, że mam dwa obiekty listy.
List 1 List 2
1 12
2 7
3 8
4 98
5 9
6 10
7 6
Za pomocą Contains wyszuka każdą pozycję z Listy 1 na Liście 2, co oznacza, że iteracja nastąpi 49 razy !!!
Próbowałem także pracować z rzeczą podobną do SQL-IN - zapytania do modelu danych jednostki . Moje podejście polega na tworzeniu ciągów znaków w celu skomponowania dużego wyrażenia OR. To okropnie brzydkie, ale obawiam się, że teraz jest to jedyna droga.
Teraz wygląda to tak:
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());
}
}
Praca z identyfikatorami GUID w tym kontekście : jak widać powyżej, zawsze we fragmentach ciągu zapytania zawsze znajduje się słowo „GUID”. Jeśli tego nie dodasz, ObjectQuery<T>.Where
zgłasza następujący wyjątek:
Typy argumentów „Edm.Guid” i „Edm.String” są niezgodne z tą operacją., Prawie równa wyrażeniu, wiersz 6, kolumna 14.
Znalazłem to na forach MSDN, warto mieć to na uwadze.
Matthias
... czekamy na kolejną wersję .NET i Entity Framework, kiedy wszystko będzie lepiej. :)
Alternatywna metoda dla odpowiedzi BenAlabaster
Przede wszystkim możesz przepisać zapytanie w następujący sposób:
var matches = from Users in people
where Users.User_Rights == "Admin" ||
Users.User_Rights == "Users" ||
Users.User_Rights == "Limited"
select Users;
Z pewnością jest to bardziej „niewygodne” i trudne do napisania, ale działa tak samo.
Gdybyśmy mieli jakąś użyteczną metodę, która ułatwiłaby tworzenie tego rodzaju wyrażeń LINQ, bylibyśmy w biznesie.
za pomocą metody narzędziowej możesz napisać coś takiego:
var matches = ctx.People.Where(
BuildOrExpression<People, string>(
p => p.User_Rights, names
)
);
To buduje wyrażenie, które ma taki sam efekt jak:
var matches = from p in ctx.People
where names.Contains(p.User_Rights)
select p;
Ale co ważniejsze, faktycznie działa przeciwko .NET 3.5 SP1.
Oto funkcja hydrauliczna, która umożliwia:
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);
}
Nie zamierzam wyjaśniać tej metody, poza tym, że zasadniczo buduje wyrażenie predykatu dla wszystkich wartości za pomocą valueSelector (tj. P => p.User_Rights) i OR tych predykatów razem, aby utworzyć wyrażenie dla kompletności orzec
Prawdziwy przykład:
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;
Poważnie? Wy nigdy nie korzystaliście
where (t.MyTableId == 1 || t.MyTableId == 2 || t.MyTableId == 3)
Checks = NumValues * NumRows
. Ponieważ jest to obliczenie typu M * N, jeśli jedno z nich jest małe, czas wykonania każdej wymaganej kontroli również będzie krótki. Dodałem ograniczenie, aby cjm30305 wiedział, jak skonfigurować środowisko testowe, w którym pokaże, dlaczego jego rozwiązanie jest słabe.
where new[] { 1, 2, 3 }.Contains(x)
wtedy mniej porównań where (x == 1 || x == 2 || x == 3)
?