異なるグラフでオブジェクトをロードするための適切なパターンを見つける
-
06-07-2019 - |
質問
使用されているコンテキストに応じて、異なるグラフ(関連するエンティティ)を持つオブジェクトの読み込みを処理する最適な方法を見つけようとしています。
たとえば、ドメインオブジェクトのサンプルを次に示します。
public class Puzzle
{
public Id{ get; private set; }
public string TopicUrl { get; set; }
public string EndTopic { get; set; }
public IEnumerable<Solution> Solutions { get; set; }
public IEnumerable<Vote> Votes { get; set; }
public int SolutionCount { get; set; }
public User User { get; set; }
}
public class Solution
{
public int Id { get; private set; }
public IEnumerable<Step> Steps { get; set; }
public int UserId { get; set; }
}
public class Step
{
public Id { get; set; }
public string Url { get; set; }
}
public class Vote
{
public id Id { get; set; }
public int UserId { get; set; }
public int VoteType { get; set; }
}
私が理解しようとしているのは、使用方法に応じてこの情報を異なる方法で読み込む方法です。
たとえば、フロントページにはすべてのパズルのリストがあります。この時点では、パズルの解決策や、それらの解決策の手順についてはあまり気にしません(かなり手間がかかります)。私が欲しいのはパズルだけです。次のようにコントローラーからロードします:
public ActionResult Index(/* parameters */)
{
...
var puzzles = _puzzleService.GetPuzzles();
return View(puzzles);
}
パズルビューについては、現在、現在のユーザーのソリューションのみを考慮しています。グラフ全体にすべてのソリューションとすべてのステップをロードしたくありません。
public ActionResult Display(int puzzleId)
{
var puzzle = _accountService.GetPuzzleById(puzzleId);
//I want to be able to access my solutions, steps, and votes. just for the current user.
}
IPuzzleService内では、メソッドは次のようになります。
public IEnumerable<Puzzle> GetPuzzles()
{
using(_repository.OpenSession())
{
_repository.All<Puzzle>().ToList();
}
}
public Puzzle GetPuzzleById(int puzzleId)
{
using(_repository.OpenSession())
{
_repository.All<Puzzle>().Where(x => x.Id == puzzleId).SingleOrDefault();
}
}
各セッションの直後にセッションが破棄されるため、遅延読み込みは実際の世界では実際には機能しません。私のコントローラーにはリポジトリの概念がないため、セッション状態を管理せず、ビューがレンダリングされるまで保持できません。
ここで使用するのに適切なパターンを理解しようとしています。 GetPuzzleWithSolutionsAndVotes
などのサービスに別のオーバーロードがありますか、 GetPuzzlesForDisplayView
や GetPuzzlesForListView
などの特定のビューがありますか?
私は理にかなっていますか?私はベースから外れていますか?助けてください。
解決
遅延読み込みを使用できない同様のケースがありました。
1つまたは2つのケースのみが必要な場合、最も簡単な方法は、個別のGetPuzleWithXYZ()メソッドを作成することです。
流fluentなインターフェイスで小さなクエリオブジェクトを作成することもできます。
次のようなもの...
public interface IPuzzleQuery
{
IPuzzleLoadWith IdEquals(int id);
}
public interface IPuzzleLoadWith
{
ISolutionLoadWith WithSolutions();
IPuzzleLoadWith WithVotes();
}
public interface ISolutionLoadWith
{
IPuzzleLoadWith AndSteps();
}
public class PuzzleQueryExpressionBuilder : IPuzzleQuery, IPuzzleLoadWith, ISolutionLoadWith
{
public int Id { get; private set; }
public bool LoadSolutions { get; private set; }
public bool LoadVotes { get; private set; }
public bool LoadSteps { get; private set; }
public IPuzzleLoadWith IdEquals(int id)
{
Id = id;
return this;
}
public ISolutionLoadWith WithSolutions()
{
LoadSolutions = true;
return this;
}
public IPuzzleLoadWith WithVotes()
{
LoadVotes = true;
return this;
}
public IPuzzleLoadWith AndSteps()
{
LoadSteps = true;
return this;
}
}
その後、リポジトリのGet()メソッドは式ビルダーをインスタンス化して呼び出し元に渡すことができます
public Puzzle Get(Action<IPuzzleQuery> expression)
{
var criteria = new PuzzleQueryExpressionBuilder();
expression(criteria);
var query = _repository.All<Puzzle>().Where(x => x.Id == criteria.Id)
if(criteria.LoadSolutions) ....
if(criteria.LoadSteps) ....
if(criteria.LoadVotes) ....
...
...
return query.FirstOrDefault();
}
通常の呼び出しは次のようになります...
Puzzle myPuzzle = Repository.Get(where => where.IdEquals(101).WithSolutions());
Puzzle myPuzzle = Repository.Get(where => where.IdEquals(101).WithSolutions().AndSteps());
Puzzle myPuzzle = Repository.Get(where => where.IdEquals(101).WithVotes().WithSolutions());
少し手間がかかりますが、基本的な考え方を見ることができます。
他のヒント
あなたのサービスにはビューに関する知識が必要だとは思いません。要件が変わる可能性があるため、名前をビュー名に結び付けないでください。
GetPuzzles()を呼び出すときは、ルートパズルのみをロードする必要があり、GetPuzzleById(int id)はアソシエーションを積極的にロードできます。
基準APIの例: .SetFetchMode(&quot; Solutions&quot ;, NHibernate.FetchMode.Join)
遅延ロードができない理由がわかりません。 nHibernateを使用している場合、プロパティにアクセスするとプロキシはデータベースに戻ります。コントローラーは必要なすべてのデータを取得する必要があります。ロードされていないプロキシをビューに渡さないでください。そうすると、ビューはデータをロードできない場合を処理する必要があります。
したがって、コントローラーに必要な特定のデータをロードさせる必要があります。ユーザーのみにロードするには、次のようにインターフェースを変更します: GetPuzzle(puzzleId、user)
この方法では、1人のユーザーのデータを簡単に読み込むことができます。