エンティティタイプに振る舞いを入れるのは間違いだと思います。
Entity FrameworkはEntity Data Modelをベースにしており、アーキテクトの1人が「.NETのオブジェクトデータモデルに非常に近い」と説明しています。言い換えれば、エンティティ・モデルは、リレーショナル・データをオブジェクト・スペースにマップするように設計されていますが、メソッドで拡張するべきではありません。ビジネスタイプのメソッドを保存します。
他のORMとは異なり、ブラックボックスからどのようなオブジェクトタイプが出てきても構いません。エンティティタイプとは異なる形になっていても、LINQを使用してほぼすべてのタイプに投影できます。ビジネスコード、データ転送、またはプレゼンテーションモデルではなく、マッピングにのみエンティティタイプを使用します。
エンティティタイプは、コード生成時に部分的に宣言されます。これにより、開発者の中にはビジネスタイプに拡張しようとする人もいます。これは間違いです。実際、エンティティタイプを拡張することはめったに良い考えではありません。エンティティモデル内で作成されたプロパティは、LINQ to Entitiesでクエリできます。部分クラスに追加するプロパティまたはメソッドは、クエリに含めることはできません。
は、ビジネス方法のこれらの例を考えてみましょう:
public Decimal CalculateEarnings(Guid id)
{
var timeRecord = (from tr in Context.TimeRecords
.Include(“Employee.Person”)
.Include(“Job.Steps”)
.Include(“TheWorld.And.ItsDog”)
where tr.Id = id
select tr).First();
// Calculate has deep knowledge of entity model
return EarningsHelpers.Calculate(timeRecord);
}
この方法で間違っているのですか?生成されたSQLは、Entity Frameworkに、Calculateメソッドで必要とされる少数のプロパティを取得するためにオブジェクト全体のインスタンスを実現するように依頼しているため、厄介な複雑さを抱くことになります。コードも脆弱です。モデルを変更するだけで、(Include呼び出しを使用して)熱心な読み込みが中断されるだけでなく、Calculateメソッドも中断されます。
単一責任原則では、クラスに変更する理由が1つだけある必要があります。画面に表示された例では、EarningsHelpersタイプは実際に収益を計算し、エンティティモデルの変更を最新の状態に保つ役割を担います。最初の責任は正しいと思われ、第二の責任は正しいとは言えません。私たちがそれを修正できるかどうかを見てみましょう。次の例で
public Decimal CalculateEarnings(Guid id)
{
var timeData = from tr in Context.TimeRecords
where tr.Id = id
select new EarningsCalculationContext
{
Salary = tr.Employee.Salary,
StepRates = from s in tr.Job.Steps
select s.Rate,
TotalHours = tr.Stop – tr.Start
}.First();
// Calculate has no knowledge of entity model
return EarningsHelpers.Calculate(timeData);
}
、Iは、計算方法の引数をロールアップ型に計算方法が必要とする情報、およびプロジェクト情報のビットのみを取り出すためにLINQクエリを書き換えています。メソッドに引数を渡すだけの新しい型を書くことが多すぎるようであれば、私は匿名型に射影し、Salary、StepRates、およびTotalHoursを個々の引数として渡すこともできました。しかしどちらの方法でも、EarningsHelpersのエンティティモデルへの依存を修正し、無料のボーナスとして、より効率的なSQLを取得しました。
TimeRecordのJobプロパティがnullの場合、このコードを見て、何が起きるのだろうかと思います。 null参照例外が発生しないでしょうか?
いいえ、私はしません。このコードはコンパイルされず、ILとして実行されません。それはSQLに変換されます。 LINQ to Entitiesはnull参照を結合します。画面に表示されているクエリの例では、StepRatesはJobがnullの場合はnullを返します。余分なデータベースクエリを除いて、これを遅延読み込みと同じと考えることができます。コードには、「仕事がある場合は、そのステップから料金を読み込んでください」と記載されています。
このようなアーキテクチャの利点は、Webアセンブリの単体テストを非常に簡単にすることです。単体テストはデータベースにアクセスすべきではなく、一般的に言えば(データベースにアクセスするテストはユニットテストではなく統合テストです)。実際にEntity Frameworkに行くのではなく、オブジェクトの配列をQueryablesとして返すモックリポジトリを書くのは簡単です。
突出部分がありませんでした。すばらしい答えがありました。私はまだ私が哲学を得るかどうかはわかりませんが。各ビジネスクラスは、必要なデータをすべてロードする代わりに、直接L2E経由で必要なデータを取得し、それを渡し、古いORMが奨励しているアプローチを保存します。 –
いいえ、ビジネスクラスがデータを直接取得する必要はないと思います。代わりに、ビジネスクラス(EarningsHelpersとEarningsCalculationContext、私の簡単な例では)は、永続性を無視する必要があります。必要なデータを照会し、それをビジネスタイプに投影し、それをビジネスメソッドに渡すサービスまたはリポジトリレイヤーがあります。はい、あなたは正しいです、私は長い間、メモリ内の多くの異なるメソッドのためにたくさんのデータを保持するという考えが嫌いです。私は共有状態がスケーラビリティの敵であると感じています。 –