web-dev-qa-db-ja.com

リポジトリが返す必要のあるデータ

コントローラーがサービスを呼び出し、必要なデータを取得するためにサービスがリポジトリーを呼び出す単純なプロジェクトがあります。

次のドメインモデルがあると仮定します。

// this model has a RepositoryA built for it
Class A {
   public string Name { get; set; }
   public int B_Id { get; set; }
}

// this model has a RepositoryB built for it
Class B {
   public string Age{ get; set; }
}

そして、このDTO:

Class MyDto {
   public string Name { get; set; }
   public string Age{ get; set; }
}

そして、リポジトリがDTOを返すべきではないこと、そしてクエリがRepositoryAで実行されなければならないことを知っている(私が間違っている場合は修正してください)。プロパティの組み合わせ(実際のプロジェクトの異なるテーブルでの複数の結合を伴う複雑な要求)、DTOでない場合、リポジトリはどのモデルを返す必要がありますか? DTOがさまざまなプロパティを受け取る準備ができていることを知っていますか?

7
Uentee

そして、リポジトリがDTOを返すべきではないことを知っている(私が間違っているなら私を訂正してください)

理論的には、すべてのレイヤー(=ソリューションのプロジェクト)には独自のDTOオブジェクトが必要です。その意味で、リポジトリはDTOを返す必要がありますが、これは「ビジネスロジックDTO」と同じDTOではありません。

ただし、実際にはそれほどの分離は必要ありません。利点は努力を上回らない。ほとんどの場合、単一エンティティーからDTOへの変換があれば十分です。これは、リポジトリー層(DAL)の1つ上の層であるビジネス層(BLL)で発生する傾向があります。
その意味で、リポジトリはDTOを返すべきではありません。しかし、あなたのビジネスクラスはそうすべきです。


プロパティの混合を含むリターンを構築するために、モデルAとBの両方を選択する必要があるRepositoryAでクエリを実行する必要があること(実際のプロジェクトのさまざまなテーブルでの複数の結合による複雑な要求)

ここでの問題はリアリズムの1つです。理論は単に実行に移すことができない。

理論的には、リポジトリはエンティティタイプ固有のデータプロバイダーになるように設計されています。つまり、AからARepositoryを取得し、BからBRepositoryを取得します。

ただし、外部ストレージリソース(データベースサーバー)を扱う場合、データを取得するために必要な労力は無視できないです。これは私たちにとって問題を引き起こします。取得するエンティティタイプごとに、独自のリポジトリを介した個別の呼び出しが必要です。

補足:外部データリソースを処理していない場合、または複数のタイプのオブジェクトを返す単一のクエリを実行しようとしていない場合は、継続この回答のうち、自分のリポジトリから各タイプを取得することに完全に満足できるので、関係はありません。

リクエストで複数のエンティティタイプを処理する場合、これらを個別の取得クエリに分離すると逆効果になります。クリーンなコード構造を作成しますが、パフォーマンスに劇的な影響を与えます。 SQLデータベースサーバーが(インデックスを使用して)結合のために特別に最適化されていることを理解すると、これは二重に悪質になりますが、データベース(リポジトリ)を呼び出すコードは、同じ優雅なデータミキシングアプローチを実装できません。

リポジトリの使用は、ここではアンチパターンになっています。あなたが遭遇する障害は、私たちがリポジトリを使用することに決めたことによって引き起こされます。そして、解決しようとするよりも大きな障害を生み出す「解決策」は解決策ではありません。


A 作業単位は、リポジトリが移行の安全性の観点から導入する多くの問題を解決します(すべてのリポジトリが同じコンテキストを使用するため)。

ただし、作業単位は、複雑なデータクエリを書き込む場所を決定するのに役立ちません(ARepositoryBRepository?...)


これは難しい選択です。ある意味で、リポジトリは特に1エンティティタイプのコンテキスト(例:単純なCRUD機能)の場合、本当に素晴らしいです。一方、複雑なデータの取得は非常に複雑になります。

私はこれについて普遍的に合意された解決策に遭遇していません。しかし、私はいくつかのプロジェクトに取り組んできましたが、コードの場所(特に複雑なデータクエリの場合)を少々センシティブに保つために、1つ(または複数)の合意に達しました。

作業単位を実装することは常に良いことです。

これは、これまでに取り組んだいくつかのプロジェクトで遭遇した合意のリストにすぎません。私は実際に一方を他方の上に置くことはできません

  1. 複雑なデータクエリは、それらのmainエンティティタイプのリポジトリに配置されます。

    • 車のリストを取得してその所有者を含むクエリは、CarRepositoryに属しています。
    • 人々のリストを取得し、彼らが所有する自動車を含むクエリはPersonRepositoryに属します。
    • Pro古いリポジトリ構造を維持できますが、少なくともコードをどこに置くかを推測するゲームを少なくすることができます。

    • Con明確な「メイン」エンティティタイプがない周辺のケースがあります。

  2. 複雑なデータクエリは、独自のリポジトリに配置されます。単一のエンティティタイプに焦点を当てた「古い」リポジトリは、CRUD操作でのみ使用する必要があります。
    • AおよびBオブジェクトを保存するメソッドがある場合、ABRepositoryおよびARepositoryを内部で使用するBRepositoryが必要です(aここで作業単位は非常に重要です!)。
    • Pro CRUDロジックとレポートロジックを分離します。
    • Con組み合わせの数が多い場合(ABACABCACD、...)リポジトリのリスト限界を超えて成長します。
    • 「con」を防ぐための提案は、これらのリポジトリにfunctionYearlyReportsRepository)の名前を付け、エンティティの集計リストだけではないことですタイプ(PersonCarRepository)。

dTOでない場合、リポジトリはどのモデルを返す必要がありますか?

オプション1を選択した場合、その質問には簡単な答えがあります。

  • 車のリストを取得してその所有者を含むクエリは、CarRepositoryに属しています。

つまり、IEnumerable<Car>を返すため、「CarRepositoryは自動車を返す」という考えに従います(おそらく関連するエンティティはいくつかありますが、戻り値の型には明示的に含まれていません)。

  • 人々のリストを取得し、彼らが所有する自動車がPersonRepositoryに属するクエリを含むクエリ。

つまり、それはIEnumerable<Person>を返すため、「PersonRepositoryは人を返す」という考えに従います(おそらく、関連するエンティティはいくつかありますが、明示的に戻り値の型の一部ではありません)。

オプション2を選択した場合の場合、複雑なデータレポートは単純なCRUD操作とは異なるものであると暗黙的に主張しています。これは、おそらくcustomクラスCarOwnerResult)がcustomから返されることを意味しますリポジトリ(=単一のエンティティタイプにバインドされません)。

必要に応じて、(単一のエンティティタイプにバインドされた)単純なリポジトリは、以前と同様に、バインドされたエンティティタイプのみを返すことが期待できます

6
Flater

リポジトリはドメインモデルを返す必要があります。

テーブルごとに1つではなく、データベースごとに1つのリポジトリが必要です。

あなたの場合、両方のドメインモデルを単一のクエリとして使用したい場合は、次のいずれかを行うことができます

  • ドメインモデルを変更して、Aが子オブジェクトBを持つようにします。
  • 2つの別個のメソッドがまだあるが、結果をキャッシュするので、2番目のメソッドが呼び出されても、クエリを再実行する必要はありません。
  • Tuple<A,B>またはDictionary<A,B>を返す

タプルを返すことは、私のオプションの中で最もお気に入りです。これを終了する場合は、おそらく代わりにComplexオブジェクトを作成する必要があります。

場合によっては、ディクショナリの戻りが意味をなすこともありますが、ここでも、常に必要とは限らない関係をコーディングするのは困難です。危険なのは、GetAwithB、GetBwithC、GetXwithQandRなどの100万のメソッドが発生することです。

2
Ewan