2

NHibernate マッピングを検証するときに同じテスト パターンを複製するのにうんざりしたので、CRUD テスト クラスに取り組んでいます。

私はコードをリファクタリングし、すべてが思い描いた通りに機能するところまで来ました。すべては、適切なリポジトリ メソッドを呼び出し、エンティティ ID などの対応するプロパティの値を取得するためにリフレクション メソッドによって使用される文字列に基づいています。

これは機能しますが、そのようなことに文字列を使用するという悪に入る必要はないと確信しています。

それで、Linq を使い始めました。私はLinqのヘビーユーザーではありませんが、次のコードには完全に困惑しています。

それはほぼ完璧に動作し (ほぼ 1 秒で表示されます)、動作することを非常に嬉しく思いますが、その理由を知りたいと思っています。

[Test]
    public void TestCanUseTesterWithLinqSecondEffort()
    {
        IRepositoryTestContextManager contextManager = new NHRepositoryTestContextManager();
        contextManager.SetUpRepositoryContextAndSchema();
        TestInsertMethodWithLinq<NHClientRepository, Client>((x, y) => x.InsertClient(y), _newClient);
        contextManager.ResetRepositoryContext();
        Client retrievedClient = TestRetrieveMethodWithLinq<NHClientRepository, Client, string>((clientRepository, publicId) => clientRepository.GetClient(publicId), client => client.PublicId, _newClient);
        contextManager.TearDownRepositoryContext();
        Assert.IsNotNull(retrievedClient);
        Assert.AreNotSame(_newClient, retrievedClient);
        Assert.AreEqual(_newClient.Id, retrievedClient.Id);
        Assert.AreEqual(_newClient.PublicId, retrievedClient.PublicId);
        Assert.AreEqual(_newClient.Name, retrievedClient.Name);
    }

    private void TestInsertMethodWithLinq<TRepositoryType, TEntityType>(Action<TRepositoryType, TEntityType> insertMethod, TEntityType entity)
        where TRepositoryType : class, new()
    {            
        insertMethod.Invoke(new TRepositoryType(), entity);
    }

    private TEntityType TestRetrieveMethodWithLinq<TRepositoryType, TEntityType, TArgumentType>(Func<TRepositoryType, TArgumentType, TEntityType> retrieveMethod, Func<TEntityType, TArgumentType> paramValue, TEntityType theEntity)
        where TRepositoryType : class, new()
    {
        return retrieveMethod.Invoke(new TRepositoryType(), paramValue(theEntity));
    }

具体的には、呼び出された 2 つのデリゲートについて話しています (デリゲートを使用するために、invoke を呼び出す必要がないことはわかっています。明確にするために含めました)。新しくインスタンス化されたクラス (この場合は TRepositoryTypes) で正しいメソッドが呼び出されるように、コンパイラは Linq 式をどのように変換するのでしょうか?

「ほぼ完全」については、呼び出されたメソッドの処理中に例外が発生した場合、その例外は飲み込まれます。それがなぜなのかはわかりませんが、例外が飲み込まれたためにテストが完了せず、問題が見逃されるシナリオをすでに確認できます。

それを噛んでください。前もって感謝します。

4

3 に答える 3

2

新しくインスタンス化されたクラス (この場合は TRepositoryTypes) で正しいメソッドが呼び出されるように、コンパイラは Linq 式をどのように変換するのでしょうか?

これらは実際には Linq 式 ( Expression<T>) ではなく、通常のラムダ式です。

コンパイラは、変数をキャプチャするために「匿名」クラスに「匿名」メソッドを作成するだけです。この場合、キャプチャされた変数がないため、次のようになります。

 TestInsertMethodWithLinq<NHClientRepository, Client>(
    (x, y) => x.InsertClient(y), _newClient
 );

単純に「匿名」メソッドに変換されます。

class __abcde {
    public void __fghij(NHClientRepository x, Client y) {
       x.InsertClient(y);
    }
 }

呼び出し元は次のように変換されます。

 var c = new __abcde();
 c.__fghij(new NHClientRepository(), _newClient);

一般的な制約new()には引数なしのコンストラクターが必要だったため、コンパイラーはそのnew NHClientRepository()ビットを挿入できました。

于 2009-08-12T02:35:28.090 に答える
0

私はNHibernateユーザー(またはその他のORM)ではないので、推測することしかできません。しかし、ここで起こっているのは、クロージャーを使用していることだと思います。クロージャを作成すると、ラムダ式で使用する変数がメソッドとともにクラスにキャプチャ/クローズオーバー/ホイストされるため、メソッドを呼び出さなくても値は最新の状態に保たれます。

于 2009-08-12T01:44:22.360 に答える
0

私は何かを見逃したかもしれませんが、ジェネリックスとポリモーフィズムについて言及していると思います。

TRepositoryタイプを渡し、すべてのTRepositoryタイプが呼び出すことができるメソッドを呼び出す必要があります。魔法はここで起こります:

TestInsertMethodWithLinq<NHClientRepository, Client>((x, y) => x.InsertClient(y), _newClient);        

このメソッドを呼び出して、タイプNHClientRepositoryおよびClientを使用すると述べています。次に、関数呼び出しを行うと、汎用メソッドは次の行でNHClientを使用します。

insertMethod.Invoke(new TRepositoryType(), entity);

これにより、TRepositoryType()がNHClientRepositoryに解決されます。次に、渡したタイプとアクションを使用してメソッドが呼び出され、結果が得られます。

何が起こっているのかを追跡したい場合は、テストにブレークポイントを設定し、呼び出しスタックを監視しながらデバッガーをステップスルーすることをお勧めします。すべてのジェネリック型やメソッド呼び出しなどを調べて、何が起こっているのかを理解するのに役立てることができます。の上。

編集:あなたのコメントを参照すると、C#3型推論がアクションを支援しています。型推論は、xとyをNHClientRepositoryとClientにマップするものであり、これにより呼び出しが機能します。コンパイラはコンパイル時に何を実行しているかを知っているので、これを支援することができます。を呼び出さずに電話をかけた場合、インテリセンスが役立つことがわかるはずですinsertMethod

それがあなたにとってパズルの最後のピースだったと思いますか?

于 2009-08-12T02:08:52.953 に答える