私は現在、同様の非データベース (この議論のサービス層オブジェクト) オブジェクトと LinqToSql を介してデータベースから取得したオブジェクトを比較するプロジェクトに取り組んでいます。この説明のために、データベースで表される文字列フィールドを持つサービス レイヤー Product オブジェクトがあると仮定します。ただし、データベースには、サービス層で表されない主キー ID もあります。
したがって (単体テストなどでよく行うように)、次のようなコードを記述できることを期待して、Equals(Object)、Equals(Product)、および GetHashCode をオーバーライドし、IEquatable を実装しました。
myContext.Products.Where(p => p.Equals(passedInProduct).SingleOrDefault();
などなど。
Equals オーバーライドはテスト済みで動作します。オブジェクトは可変であるため、GetHashCode オーバーライドには通常の注意事項が適用されます。ただし、この例では、オブジェクトは LtS 以外では変更されず、読み取り専用にすることができます。
簡単なテストは次のとおりです。
- メモリ内にテスト オブジェクトを作成し、LtS コンテキストにコミットします。コミットすると、テスト オブジェクトにいくつかの自動生成されたフィールドが入力されます。
- メモリ内に別の同一のテスト オブジェクトを作成する (別参照)
2 番目のオブジェクトを条件として使用して、データベースから最初のオブジェクトを取得しようとします。(上記のコード行を参照)。
// Setup string productDesc = "12A"; Product testProduct1 = _CreateTestProductInDatabase(productDesc); Product testProduct2 = _CreateTestProduct(productDesc); // check setup Product retrievedProduct1 = ProductRepo.Retrieve(testProduct1); //Assert.IsNotNull(retrievedProduct1); // execute - try to retrieve the 'equivalent' product object Product retrievedProduct2 = ProductRepo.Retrieve(testProduct2);
Retrieve の簡略化されたバージョン (削除されたクラフトはパラメーター チェックなどだけです):
using (var dbContext = new ProductDataContext()) {
Product retrievedProduct = dbContext.Products
.Where(p => p.Equals(product)).SingleOrDefault();
注意: オーバーライドされた Equals メソッドは、データベースから自動生成されたフィールドを気にしないことを認識しており、サービス層で表される文字列のみを調べます。
これが私が観察したことです: testProduct1 での取得は成功します (驚くことではありません。参照によって等しい) testProduct2 での取得は失敗します (null) SubmitChanges のコンテキストによって (データベースに最初のテスト オブジェクトを作成するときに呼び出されます) (期待どおりに動作します)。
静的に、コンパイラは、出力されるオブジェクトの型を認識し、型を解決できます。
だから私の具体的な質問:
- 私は何か悪いことをしようとしていますか? Equals の単純な使用法のようです。
- 最初の質問の結果: 比較の詳細をリポジトリではなくオブジェクト内に保持しながら、linq から sql への等価性チェックに対処するための代替提案
- Equals メソッドが SubmitChanges で解決されているのに、Where 句では解決されていないのはなぜですか?
- Equals 呼び出しを機能させるのと同じくらい、理解することに関心があります。しかし、LtS と C# のコンテストで「アンチパターン」と思われる理由を理解するだけでなく、この「パターン」を機能させる方法も学びたいと思っています。
Whereステートメントを使用してコンテキストを直接フィルタリングすることを提案しないでください。明らかに、Equals 呼び出しを削除してそれを行うことができます。ただし、他のオブジェクトの一部 (ここでは示していません) は大きく、少し複雑です。保守と明確化のために、理想的には問題のオブジェクトの一部として、自分自身を同じタイプの別の場所と比較する方法についての知識を保持したいと思います。
私が試した他のいくつかのことは、動作を変えませんでした:
- オーバーロードされ、代わりに == が使用されました
- ラムダ変数を p => (Product)p 型にキャストする
- 最初に IQueryable オブジェクトを取得し、Where 句で Equals を呼び出します
私が試した他のいくつかのことはうまくいきませんでした:
- 静的な ProductEquals (製品が最初、製品が 2 番目) メソッドの作成: System.NotSupportedException: SQL へのサポートされた変換がありません。
StackOverflow の貢献者に感謝します。
重複の可能性について: 他に ~10 の質問を読みました。正確な複製へのポインタが欲しいのですが、ほとんどはLinqToSqlの奇妙な点に直接対処していないようです。