Нахождение правильного шаблона для загрузки объектов с разными графиками - PullRequest
1 голос
/ 22 июня 2009

Я пытаюсь выяснить, как лучше всего обрабатывать загрузку объектов с помощью различных графиков (связанных объектов) в зависимости от контекста их использования.

Например, вот пример моих доменных объектов:

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?

Имею ли я смысл? Я далеко от базы? Пожалуйста, помогите.

Ответы [ 2 ]

2 голосов
/ 20 августа 2009

У меня был похожий случай, когда я не мог использовать Ленивую загрузку.

Если вам нужен только один или два случая, то проще всего, как вы предлагаете, создать отдельные методы 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;
    }
}

тогда ваш метод Get () Repository может создать экземпляр построителя выражений и передать его вызывающей стороне

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

это требует небольшой работы, но вы можете увидеть основную идею.

0 голосов
/ 22 июня 2009

Я не думаю, что ваш сервис должен иметь какие-либо знания о представлениях. Ваши требования могут измениться, поэтому не связывайте их имена для просмотра имен.

Когда вы вызываете GetPuzzles (), вы должны загружать только корневую головоломку, а GetPuzzleById (int id) может загружать ассоциации.

Например, в критериях API: .SetFetchMode("Solutions", NHibernate.FetchMode.Join)

Я не понимаю, почему ты не можешь ленивый груз. Если вы используете nHibernate, прокси-сервер вернется к базе данных для вас, когда вы получите доступ к свойству. Ваш контроллер должен получить все необходимые данные. Вы не должны передавать незагруженный прокси вашему представлению, тогда ваше представление должно обрабатывать случай, когда он не может загрузить данные.

Таким образом, у вас должен быть контроллер, загружающий определенные части данных, которые вам нужны, для загрузки только для пользователя, я бы изменил интерфейс так, чтобы: GetPuzzle(puzzleId,user)

В этом методе вы можете просто загрузить данные для одного пользователя.

...