Pracujemy nad Log Viewer. Użycie będzie mieć opcję filtrowania według użytkownika, poziomu ważności itp. W dniach Sql dodałem do ciągu zapytania, ale chcę to zrobić z Linq. Jak mogę warunkowo dodać klauzule where?Warunkowe zapytania Linq
Odpowiedz
jeśli chcesz tylko przefiltrować, jeśli pewne kryteria są przekazywane, zrobić coś takiego
var logs = from log in context.Logs
select log;
if (filterBySeverity)
logs = logs.Where(p => p.Severity == severity);
if (filterByUser)
logs = logs.Where(p => p.User == user);
Doing więc w ten sposób pozwoli drzewo Expression się dokładnie, co chcesz. W ten sposób stworzony SQL będzie dokładnie tym, czego potrzebujesz i niczym mniej.
Wystarczy użyć C# 's & & operatora:
var items = dc.Users.Where(l => l.Date == DateTime.Today && l.Severity == "Critical")
EDIT: Ah, trzeba bardziej uważnie przeczytać. Chcesz wiedzieć, jak warunkowo dodać dodatkowe punkty. W takim razie nie mam pojęcia. :) Najprawdopodobniej po prostu przygotuję kilka zapytań i wykonam właściwą, w zależności od tego, czego potrzebowałem.
Można użyć metody zewnętrzne:
var results =
from rec in GetSomeRecs()
where ConditionalCheck(rec)
select rec;
...
bool ConditionalCheck(typeofRec input) {
...
}
to będzie działać, ale nie może być podzielone na drzewach ekspresji, co oznacza, LINQ to SQL byłoby uruchomić kod sprawdzić przed każdym rekordzie.
Alternatywnie:
var results =
from rec in GetSomeRecs()
where
(!filterBySeverity || rec.Severity == severity) &&
(!filterByUser|| rec.User == user)
select rec;
To może działać na drzewach ekspresji, czyli LINQ do SQL byłyby zoptymalizowane.
No, co myślałem, że można umieścić warunki filtr do ogólnej listy predykatów:
var list = new List<string> { "me", "you", "meyou", "mow" };
var predicates = new List<Predicate<string>>();
predicates.Add(i => i.Contains("me"));
predicates.Add(i => i.EndsWith("w"));
var results = new List<string>();
foreach (var p in predicates)
results.AddRange(from i in list where p.Invoke(i) select i);
Wynika to w wykazie zawierającym „mnie”, „meyou” i „kosić”.
Można to zoptymalizować, wykonując foreach z predykatami w zupełnie innej funkcji niż wszystkie predykaty.
Nie jest to najładniejsza rzecz, ale można użyć wyrażenia lambda i opcjonalnie przekazać warunki. W TSQL zrobić wiele z następujących czynności, aby parametry opcjonalne:
WHERE pole = @FieldVar LUB @FieldVar IS NULL
Można powielać ten sam styl z następujących lambda (przykład sprawdzania autentykacji):
MyDataContext db = new MyDataContext();
void RunQuery (string param1, string param2, int? Param3) {
Func Checkuser = user =>
((param1.Length> 0)? Użytkownika.Param1 == param1: 1 == 1) & &
((param2.Length> 0) user.Param2 == param2: 1 == 1) & &
(! (Param3 = null) ? user.Param3 == param3: 1 == 1);
Użytkownik znalazł użytkownika = db.Users.SingleOrDefault (checkUser);
}
Jeśli chodzi o LINQ warunkowego, jestem bardzo lubiący filtrów i rur wzorca.
http://blog.wekeroad.com/mvc-storefront/mvcstore-part-3/
Zasadniczo tworzysz metodę rozszerzenia dla każdego przypadku filtra, który pobiera IQueryable i parametr.
public static IQueryable<Type> HasID(this IQueryable<Type> query, long? id)
{
return id.HasValue ? query.Where(o => i.ID.Equals(id.Value)) : query;
}
Inną opcją byłoby użyć coś takiego PredicateBuilder omówione here. To pozwala na pisanie kodu tak:
var newKids = Product.ContainsInDescription ("BlackBerry", "iPhone");
var classics = Product.ContainsInDescription ("Nokia", "Ericsson")
.And (Product.IsSelling());
var query = from p in Data.Products.Where (newKids.Or (classics))
select p;
uwaga, że mam to tylko do pracy z LINQ 2 SQL. EntityFramework nie implementuje wyrażenia Invion.Invoke, które jest wymagane do działania tej metody. Mam pytanie dotyczące tego problemu: here.
To jest świetna metoda dla osób używających Business Logic Layer na szczycie ich repozytorium wraz z narzędziem takim jak AutoMapper do mapowania pomiędzy transferem danych obiekty i modele encji. Korzystanie z kreatora predykatów pozwala na dynamiczną modyfikację swojego IQueryable przed wysłaniem go do AutoMappera w celu spłaszczenia, tj. Przeniesienia listy do pamięci. Należy pamiętać, że obsługuje on także strukturę Entity. – chrisjsherm
Miałem podobne wymagania ostatnio i ostatecznie znalazłem to w jego MSDN. CSharp Samples for Visual Studio 2008
Klasy zawarte w próbce DynamicQuery do pobrania pozwala na tworzenie dynamicznych zapytań w czasie wykonywania w następującym formacie:
var query =
db.Customers.
Where("City = @0 and Orders.Count >= @1", "London", 10).
OrderBy("CompanyName").
Select("new(CompanyName as Name, Phone)");
Korzystanie może zbudować ciąg kwerendy dynamicznie w czasie wykonywania i przekazać go w czym() metoda:
string dynamicQueryString = "City = \"London\" and Order.Count >= 10";
var q = from c in db.Customers.Where(queryString, null)
orderby c.CompanyName
select c;
skończyło używając odpowiedź podobną do Daren, ale z interfejsem IQueryable:
IQueryable<Log> matches = m_Locator.Logs;
// Users filter
if (usersFilter)
matches = matches.Where(l => l.UserName == comboBoxUsers.Text);
// Severity filter
if (severityFilter)
matches = matches.Where(l => l.Severity == comboBoxSeverity.Text);
Logs = (from log in matches
orderby log.EventTime descending
select log).ToList();
To buduje kwerendę przed pójściem do bazy danych. Polecenie nie będzie działać do końca .ToList().
Jeśli trzeba filtrować bazę na liście/Array użyć następujących:
public List<Data> GetData(List<string> Numbers, List<string> Letters)
{
if (Numbers == null)
Numbers = new List<string>();
if (Letters == null)
Letters = new List<string>();
var q = from d in database.table
where (Numbers.Count == 0 || Numbers.Contains(d.Number))
where (Letters.Count == 0 || Letters.Contains(d.Letter))
select new Data
{
Number = d.Number,
Letter = d.Letter,
};
return q.ToList();
}
To zdecydowanie najlepsza i najbardziej poprawna odpowiedź. Warunkowe || porównuje tylko pierwszą część i pomija drugą, jeśli pierwsza część jest prawdziwa ... ładnie wykonana! –
Ta konstrukcja zawiera "lub" część wyrażenia w wygenerowanym zapytaniu SQL. Przyjęta odpowiedź wygeneruje bardziej wydajne stwierdzenia. Oczywiście w zależności od optymalizacji dostawcy danych. LINQ-SQL może mieć lepszą optymalizację, ale LINQ-to-Entities nie. – Suncat2000
ten sposób:
bool lastNameSearch = true/false; // depending if they want to search by last name,
Mając to na rachunku where
:
where (lastNameSearch && name.LastNameSearch == "smith")
oznacza, że po utworzeniu zapytania końcowego, jeślijest false
zapytanie całkowicie pominie wszelkie zapytania SQL dla wyszukiwania według nazwiska.
W zależności od dostawcy danych. LINQ-to-Entities nie optymalizuje go tak dobrze. – Suncat2000
Witam Czy masz jakieś sugestie co do wprowadzenia klauzul WHO zamiast OR? –
Tak ... to trochę trudne. Najlepsze, co widziałem, to poprzez wzorzec specyfikacji i wyciągnięcie predykatu do specyfikacji, a następnie wywołanie specyfikacji.Or (someOtherSpecification). Zasadniczo trzeba trochę napisać własne drzewo ekspresji. Przykładowy kod i wyjaśnienie tutaj: http://codeinsanity.com/archive/2008/08/13/implementing-repository-and-specification-patterns-using-linq.aspx –
Mam głupie pytanie, jeśli te dzienniki są pozyskiwane z bazy danych, czy otrzymujemy wszystkie logi, a następnie filtrujemy je w pamięci? Jeśli tak, to w jaki sposób mogę przekazać warunki do bazy danych –