Pracujemy nad przeglądarką dziennika. Użycie będzie miało opcję filtrowania według użytkownika, ważności itp. W dniach Sql dodałbym do ciągu zapytania, ale chcę to zrobić za pomocą Linq. Jak mogę warunkowo dodać klauzule gdzie?
Odpowiedzi:
jeśli chcesz filtrować tylko po spełnieniu określonych kryteriów, zrób 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);
Dzięki temu drzewo wyrażeń będzie dokładnie takie, jakie chcesz. W ten sposób utworzony SQL będzie dokładnie tym, czego potrzebujesz i niczym mniej.
LINQ to Entities does not recognize the method 'System.String get_Item(System.String)' method, and this method cannot be translated into a store expression.
Jeśli chcesz filtrować na podstawie listy / tablicy, użyj 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();
}
Skończyłem używając odpowiedzi podobnej do odpowiedzi Darena, 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 tworzy zapytanie przed trafieniem do bazy danych. Polecenie nie zostanie uruchomione do końca .ToList ().
Jeśli chodzi o linq warunkowy, bardzo lubię filtry i układ rur.
http://blog.wekeroad.com/mvc-storefront/mvcstore-part-3/
Zasadniczo tworzysz metodę rozszerzającą dla każdego przypadku filtru, który przyjmuje 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;
}
Rozwiązałem to za pomocą metody rozszerzenia, aby umożliwić warunkowe włączenie LINQ w środku płynnego wyrażenia. Eliminuje to potrzebę dzielenia wyrażenia na if
instrukcje.
.If()
metoda rozszerzenia:
public static IQueryable<TSource> If<TSource>(
this IQueryable<TSource> source,
bool condition,
Func<IQueryable<TSource>, IQueryable<TSource>> branch)
{
return condition ? branch(source) : source;
}
Dzięki temu możesz to zrobić:
return context.Logs
.If(filterBySeverity, q => q.Where(p => p.Severity == severity))
.If(filterByUser, q => q.Where(p => p.User == user))
.ToList();
Oto także IEnumerable<T>
wersja, która będzie obsługiwać większość innych wyrażeń LINQ:
public static IEnumerable<TSource> If<TSource>(
this IEnumerable<TSource> source,
bool condition,
Func<IEnumerable<TSource>, IEnumerable<TSource>> branch)
{
return condition ? branch(source) : source;
}
Inną opcją byłoby użycie omawianego tutaj PredicateBuildera . Pozwala pisać kod podobny do następującego:
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;
Zauważ, że mam to tylko do pracy z Linq 2 SQL. EntityFramework nie implementuje Expression.Invoke, co jest wymagane, aby ta metoda działała. Mam pytanie dotyczące tego problemu tutaj .
Robiąc to:
bool lastNameSearch = true/false; // depending if they want to search by last name,
mając to w where
oświadczeniu:
where (lastNameSearch && name.LastNameSearch == "smith")
oznacza, że po utworzeniu ostatniego zapytania, jeśli lastNameSearch
jest, false
zapytanie całkowicie pominie dowolny kod SQL podczas wyszukiwania nazwiska.
Nie jest to najpiękniejsza rzecz, ale możesz użyć wyrażenia lambda i opcjonalnie przekazać swoje warunki. W TSQL wykonuję wiele następujących czynności, aby parametry były opcjonalne:
WHERE Field = @FieldVar LUB @FieldVar IS NULL
Możesz zduplikować ten sam styl z następującą lambdą (przykład sprawdzania uwierzytelniania):
MyDataContext db = new MyDataContext ();
void RunQuery (string param1, string param2, int? param3) {
Func checkUser = user =>
((param1.Length> 0)? user.Param1 == param1: 1 == 1) &&
((param2.Length> 0)? user.Param2 == param2: 1 == 1) &&
((param3! = null)? user.Param3 == param3: 1 == 1);
User foundUser = db.Users.SingleOrDefault (checkUser);
}
Ostatnio miałem podobne wymaganie i ostatecznie znalazłem to w MSDN. Przykłady CSharp dla programu Visual Studio 2008
Klasy zawarte w pobranym przykładzie DynamicQuery umożliwiają 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)");
Używając tego, możesz dynamicznie budować ciąg zapytania w czasie wykonywania i przekazywać go do metody Where ():
string dynamicQueryString = "City = \"London\" and Order.Count >= 10";
var q = from c in db.Customers.Where(queryString, null)
orderby c.CompanyName
select c;
Po prostu użyj operatora && w C #:
var items = dc.Users.Where(l => l.Date == DateTime.Today && l.Severity == "Critical")
Edycja: Ach, przeczytaj uważniej. Chciałeś wiedzieć, jak warunkowo dodawać dodatkowe klauzule. W takim razie nie mam pojęcia. :) To, co prawdopodobnie zrobiłbym, to po prostu przygotować kilka zapytań i wykonać właściwe, w zależności od tego, czego potrzebowałem.
Możesz użyć metody zewnętrznej:
var results =
from rec in GetSomeRecs()
where ConditionalCheck(rec)
select rec;
...
bool ConditionalCheck( typeofRec input ) {
...
}
To zadziałałoby, ale nie można go podzielić na drzewa wyrażeń, co oznacza, że Linq to SQL uruchomiłoby kod kontrolny na 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ć w drzewach wyrażeń, co oznacza, że Linq do SQL zostanie zoptymalizowany.
Cóż, pomyślałem, że możesz umieścić warunki filtru na ogólnej liście 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);
W rezultacie powstaje lista zawierająca „ja”, „meyou” i „mow”.
Możesz to zoptymalizować, wykonując foreach z predykatami w zupełnie innej funkcji, która łączy wszystkie predykaty.