4

EntityFramework5とOracleDBでパフォーマンスの問題があります。

単純なSQL選択があります:SELECT * FROM NOTE WHERE NOTENUMBER = '1A23456'

NOTENUMBERNOTEというテーブルのインデックスに含まれていますが、フィールドは主キー/一意ではありません。

  • Oracle SQL Developerを使用してステートメントを実行すると、結果がすぐに返され、クエリ・プランにRANGESCANが正常に使用されていることが示されます。

  • Entity Frameworkを使用すると、生成されるSQLにかかる時間が大幅に長くなります(5秒対30ミリ秒)。

  • Entity Frameworkを使用し、主キーフィールド(NOTE_KEY)でクエリを実行すると、SQLDeveloperの場合と同じように結果が返されます。

私は2つのことを疑っています:

  • EFおよびOracle.DataAccess-providerが使用可能なnon-unique-indexを使用していないことに問題があります。Entity Framework 5のデバッグシンボルがあれば役に立ちますが、どこにも見つかりません。

  • パフォーマンスの問題は、クロージャやEFでジェネリックリポジトリパターンを使用する方法に関して、EFのどこかにあります。

    リポジトリを次のように呼び出すと、
    var notenumber = "1A23456";
    var notes = repository.All(n => n.NOTENUMBER == notenumber).ToList();
    述語は次のようにメソッドAllに入ります。
    {n => (n.NOTE == value(Tester.Program+<>c__DisplayClass0).notenumber)}
    そして、EfProf-profilerは結果のSQLを次のようにトレースします。

    SELECT "Extent1"."NOTE_KEY" AS "NOTE_KEY",
    "Extent1"."NOTENUMBER" AS "NOTENUMBER",
    "Extent1"."NOTETEXT" AS "NOTETEXT",
    FROM "NOTE_DBA"."NOTE" "Extent1"
    WHERE ("Extent1"."NOTENUMBER" = '1PSA0500237500' /* @p__linq__0 */)

    また、クエリにかかる時間は約5500ミリ秒です。


    一方、リポジトリを次のように呼び出すと
    var notes = repository.All(n => n.NOTENUMBER == "1A23456").ToList();
    、述語は次のよう
    {n => (n.NOTENUMBER == "1A23456")}
    になります。EfProf-profilerは、結果のSQLを次のようにトレースします。

    SELECT "Extent1"."NOTE_KEY" AS "NOTE_KEY",
    "Extent1"."NOTENUMBER" AS "NOTENUMBER",
    "Extent1"."NOTETEXT" AS "NOTETEXT",
    FROM "NOTE_DBA"."NOTE" "Extent1"
    WHERE ('1PSA0500237500' = "Extent1"."NOTENUMBER")

    また、クエリには約30ミリ秒かかります。

    したがって、唯一の違いは、WHERE句の条件の順序と、後者ではEFに置き換えられたパラメーターがないように見えるという事実です。


私はVS2010と.NET4を使用し、EF5(v4.4.0.0)を参照しています。リポジトリのAll-methodは次のとおりです。

public IQueryable<NOTE> All(Expression<Func<NOTE, bool>> predicate = null)
{
    var setOfNotes = GetDbSet<NOTE>();
    var notesQuery = from note in setOfNotes select note;
    if (predicate != null)
    {
        notesQuery = notesQuery.Where(predicate);
    }
    return notesQuery;
}

CompiledQueryを作成し、を使用して、.NET4.5setOfNotes.AsNoTracking()をターゲットにしようとしました。パフォーマンスに違いはありません。

この特定のクエリをすばやく取得する方法の1つは、Oracleの基本的なData Provider for .NET(ODB.NET)を使用して手動でクエリを作成することでしたが、そのソリューションに固執することはしませんでした。繰り返しますが、where句でプライマリフィールドを使用すると、EFと同じAllメソッドを使用してもクエリは高速になります。

したがって、問題はEFのどこかにあるようです。EntityFramework.dllのシンボルだけがあれば、もっと多くのことを知ることができると思います。

EFが述語を呼び出す方法に問題がありますか?'@p_ linq _0'パラメータはEF内でどのように置き換えられますか?

4

3 に答える 3

5

私も同様の問題を抱えていました。私の場合、インデックスが使用されなかった理由は、.NETからの文字列(Unicode)をパラメーターとして渡していたためです。これは、非ユニコードデータベースフィールドと比較されました。

解決策は、文字列パラメーターを非ユニコードに変換してから、where句に渡すことでした。

using System.Data.Objects;

EntityFunctions.AsNonUnicode( myUnicodeParam)
于 2012-10-12T09:28:58.373 に答える
0

ToTraceStringメソッドを使用して生成されたSQLEntityFrameworkを確認するには、http://msdn.microsoft.com/en-us/library/system.data.objects.objectquery.totracestring.aspxを参照してください。

NOTEテーブルに関係があり、積極的な読み込みがオンになっている可能性があります。その場合、EFで生成されたSQLは、関連するすべてのデータをロードしようとします。

于 2012-08-29T17:18:57.473 に答える
0

コードにいくつかのバグと設計上の問題があります。

まず、カスタムの「すべて」のメソッド。すべてのメモをnotesQueryに選択するコード行は、何もしません。

var notesQuery = from note in setOfNotes select note;

'setOfNotes'はすでにNote型のIQueryableであることに注意してください。このステートメントは、「すべてのメモを選択するからすべてのメモを選択する」と効果的に述べています。LINQはこのステートメントを内部で完全に削除するため(わずかなパフォーマンスコストがかかります)、コードから安全に削除できます。

関数を次のように変更してみてください。

public IQueryable<NOTE> All(Expression<Func<NOTE, bool>> predicate = null)
{
    var setOfNotes = GetDbSet<NOTE>();
    return (predicate == null) ? setOfNotes : setOfNotes.Where(predicate);
}

設計には、より大きな根本的な問題があります。.NETコレクションの場合、メソッド "All"を使用して、セット内のすべての要素に対して述語が真であるかどうかを判別します。「すべての商品を返品する」という意味ではありません。確かに、独自のリポジトリを作成していますが、名前は標準の.NETの使用法と競合しています。実際には、カスタムリポジトリを完全に削除することをお勧めします。

LINQは「統合言語クエリ」です。要点は、コードと統合することです。別のレイヤーをその周りにラップすることは無意味であり、直感に反します。DbSetはIQueryableインターフェイスを実装します-それを直接公開し、コードでクエリします。これにより、LINQはクエリをより効率的にキャッシュし、冗長性を回避できます。IQueryableすでにリポジトリであり、別のリポジトリを作成する意味はありません。LINQの機能を捨てて、ストアドプロシージャの時代に戻りました。

複数の場所で複雑なクエリを使用している場合は、別のIQueryableを返す拡張メソッドをIQueryableに記述してください。次に、GetDbSet()。SliceAndDice()を実行できます。

元の問題に戻ると、割り当てと比較が混在している可能性があります。貼り付けたコードは次のとおりです。

var notes = repository.All(n => n.NOTENUMBER = notenumber).ToList();

これは正しくありません。同等性をテストするのではなく、n.NOTENUMBERプロパティに「notenumber」を割り当てようとしています。おそらくそれはスタックオーバーフローポストのタイプミスでした。代わりに、次を試してください。

var notes = repository.All(n => n.NOTENUMBER == notenumber).ToList();

またはさらに良い:

var notes = GetDbSet<NOTE>().Where(n => n.NOTENUMBER == notenumber).ToList();

同等性のバグは単なるタイプミスだったと思いますが、問題はIQueryableリポジトリ上にカスタムリポジトリを「階層化」し、それらの間で式を渡す方法にある可能性があり、EFのキャッシュ機能を適切に台無しにしています。

于 2012-09-09T04:08:10.713 に答える