문제
우리는 로그 뷰어를 개발 중입니다.사용자, 심각도 등을 기준으로 필터링할 수 있는 옵션이 제공됩니다.SQL 시절에는 쿼리 문자열에 추가했지만 Linq를 사용하고 싶습니다.조건부로 where-clause를 추가하려면 어떻게 해야 합니까?
해결책
특정 기준을 통과한 경우에만 필터링하려면 다음과 같이 하십시오.
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이 정확히 필요한 것이 될 것입니다.
다른 팁
목록/배열을 기준으로 필터링해야 하는 경우 다음을 사용하세요.
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();
}
나는 Daren의 답변과 유사하지만 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()가 실행될 때까지 실행되지 않습니다.
조건부 Linq의 경우 필터와 파이프 패턴을 매우 좋아합니다.
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;
}
또 다른 옵션은 논의된 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이 완전히 생략됩니다.
유창한 표현 도중에 LINQ를 조건부로 활성화할 수 있도록 확장 메서드를 사용하여 이 문제를 해결했습니다.이렇게 하면 표현식을 다음과 같이 분리할 필요가 없습니다. if
진술.
.If()
확장 방법:
public static IQueryable<TSource> If<TSource>(
this IQueryable<TSource> source,
bool condition,
Func<IQueryable<TSource>, IQueryable<TSource>> branch)
{
return condition ? source : branch(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 ? source : branch(source);
}
가장 좋은 것은 아니지만 람다 식을 사용하고 선택적으로 조건을 전달할 수 있습니다.TSQL에서는 매개변수를 선택적으로 만들기 위해 다음과 같은 작업을 많이 수행합니다.
필드 = @FieldVar 또는 @FieldVar가 NULL인 경우
다음 람다를 사용하여 동일한 스타일을 복제할 수 있습니다(인증 확인 예).
MyDataContext db = new MyDataContext();
void RunQuery(문자열 param1, 문자열 param2, int?매개변수3){
Func 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에서 이것을 발견했습니다.Visual Studio 2008용 CSharp 샘플
다운로드의 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);
그러면 "me", "meyou" 및 "mow"가 포함된 목록이 생성됩니다.
모든 조건자를 OR하는 완전히 다른 함수에서 조건자를 사용하여 foreach를 수행하여 이를 최적화할 수 있습니다.
이 확장 방법을 만들고 사용할 수 있습니다.
public static IQueryable<TSource> WhereIf<TSource>(this IQueryable<TSource> source, bool isToExecute, Expression<Func<TSource, bool>> predicate)
{
return isToExecute ? source.Where(predicate) : source;
}