LINQ または Entity Framework が無制限の SQL ステートメントを作成する
-
26-09-2019 - |
質問
私はアプリケーションの速度の最適化に取り組んでいますが、LINQ (または EF) が動作が遅い奇妙な SQL を作成していることがわかりました。
ここにいくつかのコードがあります:
SomeList.AddRange(_databaseView
.Select(l=> new SomeViewModel
{
Date = l.Date,
Details = l.Details,
Level = l.LevelName,
Id = l.ViewID,
Message = l.Message,
ProjectName = l.projectName,
StatusId = l.StatusID,
StatusName = l.StatusName
})
.Skip(50)
.Take(25));
そして理論的には、25レコードを取るSQLステートメントを作成するはずですが、プロファイラーはそれに対して次のSQLを表示します。
SELECT [Extent1].[Date] AS [Date],
[Extent1].[ID] AS [ID],
[Extent1].[LevelID] AS [LevelID],
[Extent1].[StatusID] AS [StatusID],
[Extent1].[projectName] AS [projectName],
[Extent1].[LevelName] AS [LevelName],
[Extent1].[StatusName] AS [StatusName],
[Extent1].[Message] AS [Message],
[Extent1].[Details] AS [Details],
[Extent1].[LogViewID] AS [LogViewID]
FROM (SELECT [v_MyView].[Date] AS [Date],
[v_MyView].[ProjectID] AS [ProjectID],
[v_MyView].[LevelID] AS [LevelID],
[v_MyView].[StatusID] AS [StatusID],
[v_MyView].[projectName] AS [projectName],
[v_MyView].[LevelName] AS [LevelName],
[v_MyView].[StatusName] AS [StatusName],
[v_MyView].[Message] AS [Message],
[v_MyView].[Details] AS [Details],
[v_MyView].[ViewID] AS [ID]
FROM [dbo].[v_MyView] AS [v_MyView]) AS [Extent1]
_databaseView は、すべての並べ替えとフィルター処理のロジックが実行される IQueryable オブジェクトです。
ここに私が考えたことがあります:フィルタリングを行わない場合、SQL は SELECT TOP (25) を使用して正常に実行されます。しかし、フィルタリングを行うたびに、何かが台無しになります。これは私のフィルターの1つのコードです:
if (Filters.ProjectName != null && Filters.ProjectName[0] != 0) // check if "all" is not checked
_databaseView = Filters.ProjectName
.Join(_databaseView, f => f, l => l.ProjectID, (f,l) => new SomeViewModel
{
Date = l.Date,
Details = l.Details,
LevelName = l.LevelName,
ViewID = l.ViewID,
Message = l.Message,
projectName = l.projectName,
StatusID = l.StatusID,
StatusName = l.StatusName
})
.AsQueryable();
そしてそれは何の制約もありません。この LINQ-EF を使って適切な SQL を生成するにはどうすればよいでしょうか?
事前にお願いします!
解決
あなたは_DatabaseView
が何であるかを言うことはありませんが、あなたの結果に基づいて、私の野生の推測では、それはObjectQuery<T>
ではないということです。これはあなたの問題を説明するだろう。 SQLに変換しますObjectQuery
。 IEnumerable<T>.Skip()
はしません。列挙上AsQueryable()
を呼び出すと、これを実現するのに十分なのないのです。
たとえば、この:
var foo = MyObjectContext.SomeEntitySet.AsEnumerable().AsQueryable().Take(10);
... SQLでTOP
を入れません。
しかし、これます:
var bar = MyObjectContext.SomeEntitySet.Take(10);
...意志ます。
もう一度:あなたは何_DatabaseView
がの言っていません。あなたのObjectContext
上で直接この操作を試してみて、あなたはそれが動作することを確認できます。バグはあなたが私たちを示していないアサイン_DatabaseView
に使用したコードである。
他のヒント
あなたが実際に使用されているSQLを変更することができます唯一の方法ではなく、生成されたSQLを使用することを自分自身と使用を書くことになります。
あなたはスキップを意味し、SQLに変換されていないLINQの部分を取ります。私はそれが原因であなたがLINQをやっていることな方法であると思います。
タグのようなものを試してみてください
(From l In DataBaseView Select new SomeViewModel
{
Date = l.Date,
Details = l.Details,
Level = l.LevelName,
Id = l.ViewID,
Message = l.Message,
ProjectName = l.projectName,
StatusId = l.StatusID,
StatusName = l.StatusName
}).Skip(50).Take(25)
その代わり、それが生成されたコードの違いを作るかどうかを確認します。
編集どういうわけか、私はあなたはそれが25回の記録を取るSQLべきであると述べた部分を逃しています。
LINQパーサは間違いエンティティクエリにあなたのLINQでSkip
とTake
方法を検討し、正しい式ツリーを生成し、のオブジェクトサービスのは、データベースに渡されるコマンドツリーに式ツリーを変換し、特定のSQLクエリを生成するためのプロバイダ。
この場合、この2つの方法は、それぞれWHERE [Extent1].[row_number] > 50
とSELECT TOP (25)
ためSkip
とTake
で生成されたSQLに影響を与えます。
さて、あなたは、のプロファイラの右のトレースを見ていることを確信していますか?私はの <強いを見てみましょう示唆します> ObjectQuery.ToTraceString のののメソッドのプロファイラそして、あなたのコードをデバッグし、のSQL の値を調べem>の変数ます:
var query = _DatabaseView.Select(l=> new SomeViewModel {
Date = l.Date,
Details = l.Details,
Level = l.LevelName,
Id = l.ViewID,
Message = l.Message,
ProjectName = l.projectName,
StatusId = l.StatusID,
StatusName = l.StatusName})
.Skip(50)
.Take(25));
string sql = (query as ObjectQuery).ToTraceString();
あなたは常に、ストアドプロシージャを書いてみてください可能性があり、ちょうどLINQからのことを呼び出します。
スキップを動かしてみて、選択の前に取ります。