Ми працюємо над засобом перегляду журналів. Використання матиме можливість фільтрувати за користувачем, ступенем тяжкості тощо. За днів Sql я додав би до рядка запиту, але я хочу зробити це за допомогою Linq. Як я можу умовно додати де-речення?
Відповіді:
якщо ви хочете фільтрувати лише за умови передачі певних критеріїв, зробіть щось подібне
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);
Якщо зробити це таким чином, дерево виразів зможе бути саме тим, що ви хочете. Таким чином створений SQL буде саме тим, що вам потрібно, і не менше.
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.
Якщо вам потрібно відфільтрувати основу за списком / масивом, використовуйте наступне:
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();
}
Я закінчив, використовуючи відповідь, схожу на відповідь Дарена, але з інтерфейсом 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();
Це створює запит перед потраплянням у базу даних. Команда не запускатиметься до кінця .ToList ().
Що стосується умовної лінки, я дуже люблю фільтри та схему труб.
http://blog.wekeroad.com/mvc-storefront/mvcstore-part-3/
В основному ви створюєте метод розширення для кожного випадку фільтра, який приймає IQueryable та параметр.
public static IQueryable<Type> HasID(this IQueryable<Type> query, long? id)
{
return id.HasValue ? query.Where(o => i.ID.Equals(id.Value)) : query;
}
Я вирішив це методом розширення, щоб дозволити LINQ умовно активувати в середині вільного виразу. Це позбавляє від необхідності розбивати вираз if
твердженнями.
.If()
метод розширення:
public static IQueryable<TSource> If<TSource>(
this IQueryable<TSource> source,
bool condition,
Func<IQueryable<TSource>, IQueryable<TSource>> branch)
{
return condition ? branch(source) : source;
}
Це дозволяє зробити це:
return context.Logs
.If(filterBySeverity, q => q.Where(p => p.Severity == severity))
.If(filterByUser, q => q.Where(p => p.User == user))
.ToList();
Ось також IEnumerable<T>
версія, яка буде обробляти більшість інших виразів LINQ:
public static IEnumerable<TSource> If<TSource>(
this IEnumerable<TSource> source,
bool condition,
Func<IEnumerable<TSource>, IEnumerable<TSource>> branch)
{
return condition ? branch(source) : source;
}
Іншим варіантом було б використовувати щось на зразок PredicateBuilder, про який тут мова . Це дозволяє писати код таким чином:
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;
Зверніть увагу, що я маю це лише для роботи з Linq 2 SQL. EntityFramework не реалізує Expression.Invoke, необхідний для роботи цього методу. У мене є запитання щодо цього питання тут .
Робимо це:
bool lastNameSearch = true/false; // depending if they want to search by last name,
маючи це у where
заяві:
where (lastNameSearch && name.LastNameSearch == "smith")
означає, що при створенні остаточного запиту, якщо lastNameSearch
є, false
запит повністю пропускає будь-який SQL для пошуку за прізвищем.
Це не найкрасивіша річ, але ви можете використовувати лямбда-вираз і передавати свої умови за бажанням. У TSQL я роблю багато наступного, щоб зробити параметри необов’язковими:
WHERE Field = @FieldVar АБО @FieldVar НУЛЬНИЙ
Ви можете скопіювати той самий стиль за допомогою такої лямбда (приклад перевірки автентичності):
MyDataContext db = новий MyDataContext ();
void RunQuery (рядок param1, рядок param2, int? param3) {
Функція checkUser = користувач =>
((param1.Length> 0)? user.Param1 == param1: 1 == 1) &&
((param2.Length> 0)? user.Param2 == param2: 1 == 1) &&
((param3! = null)? user.Param3 == param3: 1 == 1);
Користувач foundUser = db.Users.SingleOrDefault (checkUser);
}
Нещодавно я мав подібну вимогу, і врешті-решт виявив це в MSDN. Приклади CSharp для Visual Studio 2008
Класи, включені до зразка DynamicQuery завантаження, дозволяють створювати динамічні запити під час виконання у наступному форматі:
var query =
db.Customers.
Where("City = @0 and Orders.Count >= @1", "London", 10).
OrderBy("CompanyName").
Select("new(CompanyName as Name, Phone)");
Використовуючи це, ви можете динамічно створювати рядок запиту під час виконання та передавати його в метод Where ():
string dynamicQueryString = "City = \"London\" and Order.Count >= 10";
var q = from c in db.Customers.Where(queryString, null)
orderby c.CompanyName
select c;
Просто використовуйте оператор && C #:
var items = dc.Users.Where(l => l.Date == DateTime.Today && l.Severity == "Critical")
Редагувати: Ах, потрібно читати уважніше. Ви хотіли знати, як умовно додати додаткові речення. У такому випадку я поняття не маю. :) Що б я, мабуть, зробив, це просто підготувати кілька запитів і виконати правильний, залежно від того, що мені в підсумку було потрібно.
Ви можете використовувати зовнішній метод:
var results =
from rec in GetSomeRecs()
where ConditionalCheck(rec)
select rec;
...
bool ConditionalCheck( typeofRec input ) {
...
}
Це буде працювати, але не може бути розбито на дерева виразів, що означає, що Linq to SQL запускає перевірочний код для кожного запису.
В якості альтернативи:
var results =
from rec in GetSomeRecs()
where
(!filterBySeverity || rec.Severity == severity) &&
(!filterByUser|| rec.User == user)
select rec;
Це може працювати в деревах виразів, тобто Linq to SQL буде оптимізовано.
Ну, я подумав, що ви можете помістити умови фільтру в загальний список предикатів:
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);
У результаті з’являється список, що містить „я”, „мені” та „косити”.
Ви можете оптимізувати це, виконавши foreach з предикатами в абсолютно іншій функції, що АБО всі предикати.