回答:
特定の基準に合格した場合にのみフィルタリングする場合は、次のようにします
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();
}
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;
}
これを拡張メソッドで解決し、滑らかな式の途中で条件付きで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フィールド= @FieldVar OR @FieldVar IS NULL
次のラムダ(認証を確認する例)を使用して同じスタイルを複製できます。
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);
ユーザー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;
外部メソッドを使用できます:
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を実行することで、これを最適化できます。