4

EF をテストするときは、LINQ to Objects プロバイダーと LINQ to Entities プロバイダーの違いにより、ライブ DB に対して統合テストを使用する必要があると聞いています。

実際にクエリを実行せずに、クエリの構築を単体テストできないのはなぜですか? IQueryable を何らかの方法でLINQ to Entities プロバイダーにアタッチし、SQL が適切に生成されていることを確認できると思いました (実際のクエリを実行しない ToTraceString などを使用)。

これらの行に沿ったコードを想像します (このクエリは L2O では正常に実行されますが、L2E では実行されません)。

    <Test()> _
    Public Sub Query_Should_Build_Against_L2E()
        Dim testQuery = From d In myDb
                        Where d.Status = CType(Status.Ready, Integer)

        testQuery.SetQueryProvider("L2E")

        Assert.DoesNotThrow(testQuery.ToTraceString())
    End Sub

編集

GertArnold のソリューションを次のように実装しようとしました。

Dim context As New Context("Data Source=fakedbserver;Initial Catalog=fakedb;Persist Security Info=True;")
Dim result = context.myTable.Where(Function(d) d.Status=True)

ProviderIncompatibleExceptionこれにより、「データベースからプロバイダー情報を取得中にエラーが発生しました。これは、不適切な接続文字列を使用した Entity Framework が原因である可能性があります。詳細については内部例外を確認し、接続文字列が正しいことを確認してください。」というメッセージがスローされます。完全な例外チェーンは次のとおりです。

System.Data.ProviderIncompatibleException : データベースからプロバイダー情報を取得中にエラーが発生しました。これは、不適切な接続文字列を使用する Entity Framework が原因である可能性があります。内部例外で詳細を確認し、接続文字列が正しいことを確認してください。

System.Data.ProviderIncompatibleException : プロバイダーは ProviderManifestToken 文字列を返しませんでした。

System.InvalidOperationException : この操作には、'master' データベースへの接続が必要です。元のデータベース接続が開かれており、資格情報が接続文字列から削除されているため、'master' データベースへの接続を作成できません。開かれていない接続を提供します。

System.Data.SqlClient.SqlException : SQL Server への接続を確立中に、ネットワーク関連またはインスタンス固有のエラーが発生しました。サーバーが見つからないか、アクセスできませんでした。インスタンス名が正しいこと、および SQL Server がリモート接続を許可するように構成されていることを確認してください。(プロバイダー: 名前付きパイプ プロバイダー、エラー: 40 - SQL Server への接続を開けませんでした)

4

2 に答える 2

2

IQueryable を LINQ to Entities プロバイダーにアタッチします

問題は次のとおりIQueryableです。インターフェイス自体は何もありません。EF を使用すると、実装するクラスを処理しますIQueryableが、それ以外にも多くのことを行います。実際、それらは、内部で既に接続されているコンテキストおよびクエリ プロバイダーと連携するための完全な機能を備えています。IQueryable.IQueryProviderは読み取り専用のプロパティであるため、特定のIQueryable実装を作成するファクトリによって設定されます。

したがって、あなたはEF コンテキスト以外のものから取得されるため、 にtestQueryなります。ObjectQuerymyDb

実際にクエリを実行せずに、クエリの構築を単体テストできないのはなぜですか?

これはあなたの本当の質問ではありませんか?実際、そのために別のクエリ プロバイダーが必要かどうかさえ疑問に思います。このように動作する EF クエリ プロバイダーが必要だと思います。そうしないと、EF で同じように動作するという保証はまだありません。

とにかく、接続文字列に偽のデータベース名を含むコンテキストを作成し (接続しないことを確認するため)、 の有効性を確認でき((ObjectQuery)testQuery).ToTraceString()ます。繰り返さなければOKですtestQuery。クエリが複雑な実行パスで構成されている場合、このようなテストには何らかの価値があると想像できます。(しかし、私はそれを避けたいと思います)。

于 2012-07-31T23:20:01.077 に答える
0

短い答え

実際のデータベース接続なしで、適​​切な EF プロバイダーに「接続」するモック DbContext を作成できました。これにより、テスト対象の LINQ が実際のデータベースに対する統合シナリオで実際に機能することが保証されます。現在、MS SQL Server に対してのみ実装されていますが、他の EF プロバイダーに拡張するのは非常に簡単です。以下に完全なコードを掲載しました。フィードバックをお待ちしております。

EDITコードをGPL CodePlex プロジェクトに移動しました。

長い答え

GertArnoldの回答を見て、問題全体を再考した後 (およびソースを徹底的に検討した後)、実行可能な解決策と思われるものを思いつきました。問題は、DbContext の既定の構成が次のようになっていることです。

  1. デフォルトを使用して、コンストラクターのプロパティSqlConnectionFactoryによって要求されたデータベースへの接続を作成しますnameOrConnectionStringDbContext
  2. データベースが存在しない場合は作成します (CreateDatabaseIfNotExistsデフォルトのコンテキスト初期化子として使用するため)
  3. データベースへの接続を開き、アクセス可能であることを確認します (DbConnection.Openおよびを使用DbConnection.State) 。
  4. データベースにクエリを実行して、要求されたモデルがそこに存在することを確認します (これを使用しSystem.Data.Entity.Internal.InternalContext.QueryForModelて、アクティブな接続でコマンドを作成し、モデルのデータベースをクエリします)。
  5. Expression を SQL に変換するためのサポートを一緒に提供するSystem.Data.Common.DbProviderServicesとオブジェクトを構築します。System.Data.Common.DbProviderManifest

これらのステップの最初の 4 つ (実際の db 接続が必要です) はIDatabaseInitializer、メソッド内で何もしない new を作成してInitializeDatabaseから、テスト対象のコンテキスト タイプを に登録することDatabase.SetInitializerです。

一方、ステップ 5 は、選択のために実際の DbContext が必要な理由です。DbProviderServices と DbProviderManifest がないと、EF は式を変換する方法を認識できません。リフレクションを使用して内部情報を取得しSqlProviderManifestSqlProviderServices作業を彼らに渡すことができました (使用する SQL サーバーのバージョンを年ごとに指定することもできます)。

于 2012-08-08T13:46:44.000 に答える