質問
私たちはログビューアを開発中です。ユーザー、重大度などでフィルタリングするオプションがあります。SQL の時代はクエリ文字列に追加していましたが、Linq でそれを実行したいと考えています。条件付きで where 句を追加するにはどうすればよいですか?
解決
特定の基準に合格した場合にのみフィルタリングしたい場合は、次のようなことを実行します
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 では、パラメーターをオプションにするために次の多くのことを行います。
WHERE フィールド = @FieldVar または @FieldVar が NULL
次のラムダを使用して同じスタイルを複製できます (認証のチェックの例)。
MyDataContext db = 新しい MyDataContext();
void RunQuery(string param1, string param2, int?パラメータ3){
Function checkUser = ユーザー =>
((param1.Length > 0)?user.Param1 == param1 :1 == 1) &&
((param2.Length > 0)?user.Param2 == param2 :1 == 1) &&
((param3 != null)?user.Param3 == param3 :1 == 1);
ユーザーが見つかりましたUser = 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;
}