LINQ to SQLで、LINQクエリの一部を関数に渡す方法
-
10-07-2019 - |
質問
linqクエリの一部を関数に渡すことは可能ですか? 常に同じクエリインターフェイスを使用するDALの共通インターフェイスを作成したい。たとえば、
List<T> Get(Join j, Where w, Select s){
return currentDataContext<T>.Join(j).Where(w).Select(s).ToList();
}
この種のことは可能ですか?表現ツリーを使用してそれを行うと考えていますが、その例を見つけることができませんでした。
解決
まあ、<!> quot; join <!> quot;結合を表現するのは非常に難しいため、トリッキーです-ただし、where / select / orderbyなどは非常に簡単です...
実際、これはIQueryable<T>
でさまざまなLINQメソッドを組み合わせた場合にすぎず、通常はいくつかの組み合わせでExpression<Func<...>>
を受け入れます。したがって、オプションの述語を使用した基本的な選択は次のようになります。
public IQueryable<T> Get<T>(
Expression<Func<T,bool>> predicate
) where T : class
{
IQueryable<T> query = (IQueryable<T>)GetTable(typeof(T));
if (predicate != null) query = query.Where(predicate);
return query;
}
完全に構成可能であるため、ToList()
も返す傾向があります。呼び出し元がリストを必要とする場合、リストで常に<=>を使用できます...または(たとえば):
using(var ctx = new MyDataContext(CONN))
{
ctx.Log = Console.Out;
int frCount = ctx.Get<Customer>(c => c.Country == "France").Count();
}
これは(Northwindを使用して)クエリを実行します:
SELECT COUNT(*) AS [value]
FROM [dbo].[Customers] AS [t0]
WHERE [t0].[Country] = @p0
<!> quot; select <!> quot;のインクルードに関する問題。クエリの(投影)は、複数のジェネリック型になることです。多くの場合、投影を匿名型にしたいので、投影型(匿名)と を指定することはほとんど不可能であり、呼び出し可能ではありません。
実際には、このようなメソッドを作成することには大きなメリットがあるのでしょうか。基本的な方法に固執するかもしれません:
public IQueryable<T> Get<T>() where T : class
{
return (IQueryable<T>)GetTable(typeof(T));
}
そして、呼び出し元に好みの方法でそれを作らせます-おそらくクエリ構文で:
var list = (from cust in ctx.Get<Customer>()
where cust.Country == "France"
select cust.CompanyName).Take(10).ToList();
次の用途:
SELECT TOP (10) [t0].[CompanyName]
FROM [dbo].[Customers] AS [t0]
WHERE [t0].[Country] = @p0
別の方法として、オーダーバイとプロジェクションを本当に含めたい場合、拡張方法が最も実用的なアプローチです。次に、元の(ソース)Tを指定する必要はありません(これにより、非タイプと混合した場合に呼び出し不可能になります):
public static class QueryExtension
{
public static IQueryable<TProjection>
Get<TSource, TProjection, TOrderKey>(
this IQueryable<TSource> source,
Expression<Func<TSource, bool>> where, // optional
Expression<Func<TSource, TProjection>> select,
Expression<Func<TProjection, TOrderKey>> orderBy)
{
if (where != null) source = source.Where(where);
return source.Select(select).OrderBy(orderBy);
}
}
次に、次のようなDALメソッドを検討します。
public List<string> Countries()
{
return Customers.Get(
x=>x.CompanyName != "",
x=>x.Country,
x=>x).Distinct().ToList();
}
使用する(再びNorthwindで):
SELECT DISTINCT [t0].[Country]
FROM [dbo].[Customers] AS [t0]
WHERE [t0].[CompanyName] <> @p0
他のヒント
この汎用クラスを確認します: TableView.cs 。
基本的にはFunc <!> lt; TEntity、bool <!> gt;を使用しますWhere述語を適用するデリゲート:
//...
public TableView(DataContext dataContext, Expression<Func<TEntity, bool>> predicate)
{
this.table = dataContext.GetTable<TEntity>();
this.baseQuery = table.Where(predicate);
this.predicate = predicate.Compile();
}
//...
Linq Examplesで使用可能なDynamic Expressionライブラリを使用できます。この拡張ライブラリを使用すると、whereなどのlinq句を渡すことができます...
ここからダウンロードできます linqサンプル
ダイナミックLINQ を使用して、パラメータを文字列として渡します。
Marc Gravell <!>#9830;いつものように、非常に洞察力のある答えを提供しましたが、ほとんどの場合、 IQueryables を取り、制限を追加するメソッドがあるとうまくいくと思います。より明確で簡単に保守可能。例:
//Join
public static IQueryable<IContract> AllContracts(this IQueryable<IAccount> accounts, ISession s ) {
return from a in accounts
from contract in s.Query<IContract()
where (a.Id == contract.AccountId)
select contract;
}
//Where
public static IQueryable<IContract> Active(this IQueryable<IContract> contracts) {
return from contract in contracts
where (contract.Active == true)
select contract;
}
次に、次のようにこれらを組み合わせて一致させることができます。
IQueryable<IContract> activeContracts = s.Query<IAccount>()
.Where(o => o.Name == "XXX")
.GetContracts(s)
.Active();
ここでは拡張メソッドとNHiberanteのLINQが Query メソッドを提供していますが、これは静的メソッドやLINQプロバイダーを使用せずに簡単に書き換えることができます。