找到加载具有不同图形的对象的正确模式

发布于 2024-07-25 08:25:56 字数 2111 浏览 5 评论 0原文

我试图找出处理加载具有不同图形(相关实体)的对象的最佳方法,具体取决于它们所使用的上下文。

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

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)?

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

I'm trying to figure out the best way to handle loading objects with different graphs (related entities) depending on the context their being used.

For example Here's a sample of my domain objects:

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

What I'm trying to understand is how to load this information differently depending on how I'm using it.

For example, on the front page I have a list of all puzzles. At this point I don't really care about the solutions for the puzzle or for the steps in those solutions (which can get pretty hefty). All I want are the puzzles. I would load them from my controller like this:

public ActionResult Index(/*  parameters   */)
{
    ...
    var puzzles = _puzzleService.GetPuzzles();
    return View(puzzles);
}

Later on for the puzzle view I now care about only the solutions for the current user. I don't want to load the entire graph with all of the solutions and all of the steps.

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

Inside my IPuzzleService, my methods look like this:

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

Lazy loading doesn't really work in the real world, because my session is being disposed right after each unit of work. My controllers don't have any concept of the repository and therefore do not manage session state and can't hold on to it until the view is rendered.

I'm trying to figure out what the right pattern to use here is. Do I have different overloads on my service like GetPuzzleWithSolutionsAndVotes or more view specific like GetPuzzlesForDisplayView and GetPuzzlesForListView?

Am I making sense? Am I way off base? Please help.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

愁杀 2024-08-01 08:25:56

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

如果您只需要一两个案例,那么按照您的建议,最简单的方法是创建单独的 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());

它需要一些工作,但您可以看到基本思想。

I had a similar case where I could not use Lazy loading.

If you only need a one or two cases, then the easiest thing as you suggest, create separate GetPuzleWithXYZ() methods.

You could also create a small query object with a fluent interface.

Something like...

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

then your Repository Get() method can instantiate the expression builder and pass it to the caller

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

and typical calls would look like...

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());

it needs a little work, but you can see the basic idea.

雾里花 2024-08-01 08:25:56

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

当您调用 GetPuzzles() 时,您应该只加载根谜题,而 GetPuzzleById(int id) 可以立即加载关联。

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

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

所以你应该让控制器加载你需要的特定数据,只为用户加载,我会改变接口,这样: GetPuzzle(puz​​zleId,user)

在这个方法中,你可以直接加载一个用户的数据。

I don't think your service should have any knowledge of the Views. Your requirements might change so don't tie their names to view names.

When you call GetPuzzles() you should only load root puzzle, and GetPuzzleById(int id) can eager load the associations.

For example in criteria API: .SetFetchMode("Solutions", NHibernate.FetchMode.Join)

I don't understand why you cant lazy load. If your using nHibernate, the proxy will go back to the database for you when you access the property. Your controller should get all the data you need. You should not pass a non-loaded proxy to your view then your view has to handle the case where it can't load the data.

So you should have controller load the specific pieces of data you need, to load only for a user, I would change the interface such that: GetPuzzle(puzzleId,user)

In this method you can just eager load the data for the one user.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文