【问题标题】:Finding the right pattern for loading objects with different graphs找到正确的模式来加载具有不同图形的对象
【发布时间】:2009-06-22 15:06:57
【问题描述】:

我正在尝试根据使用的上下文找出处理加载具有不同图形(相关实体)的对象的最佳方法。

例如,这是我的域对象的示例:

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 或更多特定视图,例如 GetPuzzlesForDisplayViewGetPuzzlesForListView

我说得有道理吗?我离基地很远吗?请帮忙。

【问题讨论】:

    标签: .net asp.net-mvc nhibernate lazy-loading fluent-interface


    【解决方案1】:

    我有一个类似的情况,我无法使用延迟加载。

    如果您只需要一两种情况,那么按照您的建议,最简单的方法是创建单独的 GetPuzleWithXYZ() 方法。

    您还可以创建一个具有流畅界面的小型查询对象。

    类似...

    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;
        }
    }
    

    然后您的 Repository 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());
    

    它需要一点工作,但你可以看到基本的想法。

    【讨论】:

      【解决方案2】:

      我认为您的服务不应该对视图有任何了解。您的要求可能会发生变化,因此请不要将他们的姓名与查看姓名联系起来。

      当您调用 GetPuzzles() 时,您应该只加载根拼图,而 GetPuzzleById(int id) 可以预先加载关联。

      例如在标准 API 中:.SetFetchMode("Solutions", NHibernate.FetchMode.Join)

      我不明白你为什么不能延迟加载。如果您使用 nHibernate,则代理将在您访问该属性时为您返回数据库。您的控制器应该获得您需要的所有数据。您不应将未加载的代理传递给您的视图,然后您的视图必须处理无法加载数据的情况。

      所以你应该让控制器加载你需要的特定数据,只为用户加载,我会改变界面:GetPuzzle(puzzleId,user)

      在这种方法中,您可以只为一个用户预先加载数据。

      【讨论】:

      • 我的脑子现在有点模糊,希望这对你有意义并提供一些价值
      • 我不能延迟加载,因为当我的会话在 GetPuzzle...X() 方法结束时超出范围时,它会被处理掉。当代理稍后尝试获取项目时,我收到“会话已释放”错误。
      猜你喜欢
      • 1970-01-01
      • 2021-11-12
      • 2013-03-02
      • 2020-03-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-12
      • 2016-08-15
      相关资源
      最近更新 更多