Специальная структура данных и репозитория - PullRequest
5 голосов
/ 07 марта 2009

Каков рекомендуемый способ возврата из хранилища данных ad hoc (индивидуально для каждого конкретного случая), которые не соответствуют объектам модели или расширяют их?

Примером 101 было бы вездесущее приложение «здравствуйте»: система блогов. Предположим, вы хотите загрузить список публикаций, в которых запись содержит некоторую дополнительную информацию, которой нет в сущности Post. Допустим, это количество комментариев, а также дата и время последнего комментария. Это было бы очень тривиально, если бы вы использовали простой старый SQL и считывали данные непосредственно из базы данных. Как я должен сделать это оптимально, используя шаблон репозитория, если я не могу позволить себе загружать все коллекции Комментариев для каждого Поста, и я хочу сделать это одним попаданием в базу данных? Есть ли какой-либо обычно используемый шаблон для этой ситуации? Теперь представьте, что у вас есть довольно сложное веб-приложение, в котором каждой странице требуются немного разные пользовательские данные, а загрузка полных иерархий невозможна (производительность, требования к памяти и т. Д.).

Некоторые случайные идеи:

  1. Добавить список свойств для каждой модели, которые могут быть заполнены пользовательскими данными.

  2. Объекты модели подкласса для каждого конкретного случая и создание пользовательских считывателей для каждого подкласса.

  3. Использование LINQ, составление специальных запросов и чтение анонимных классов.

Примечание: я недавно задал аналогичный вопрос , но он казался слишком общим и не привлек большого внимания.

Пример:

Основываясь на предложениях в ответах ниже, я добавляю более конкретный пример. Вот ситуация, которую я пытался описать:

IEnumarable<Post> posts = repository.GetPostsByPage(1);
foreach (Post post in posts)
{

    // snip: push post title, content, etc. to view

    // determine the post count and latest comment date
    int commentCount = post.Comments.Count();
    DateTime newestCommentDate = post.Comments.Max(c => c.Date);

    // snip: push the count and date to view

}

Если я не буду делать ничего лишнего и использую готовый ORM, это приведет к n + 1 запросам или, возможно, одному запросу, загружающему все сообщения и комментарии. Но оптимально, я хотел бы иметь возможность просто выполнить один SQL, который бы возвращал одну строку для каждого поста, включая заголовок поста, тело и т. Д., А также количество комментариев и самую последнюю дату комментария в одном и том же. Это тривиально в SQL. Проблема в том, что мой репозиторий не сможет читать и вписывать данные этого типа в модель. Куда идут максимальные даты и количество?

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

Ответы [ 5 ]

1 голос
/ 07 марта 2009

Есть много вопросов к этому вопросу. Вам нужны эти конкретные данные для процедуры отчетности? Если это так, то правильным решением будет отдельный доступ к данным для целей отчетности. Уплощенные базы данных, виды и т. Д.

Или это специальный запрос? Если так, у Ayende есть пост по этой самой проблеме. http://ayende.com/Blog/archive/2006/12/07/ComplexSearchingQueryingWithNHibernate.aspx

Он использует объект "Искатель". Он использует NHibernate, поэтому, по сути, он создает отдельный запрос.

В прошлом я делал нечто подобное, создавая объект Query, который я могу заполнить до его передачи в хранилище (некоторые пуристы DDD будут спорить с этим, но я считаю его элегантным и простым в использовании).

Объект Query реализует свободный интерфейс, поэтому я могу написать это и получить результаты обратно:

IQuery query = new PostQuery()
   .WithPostId(postId)
   .And()
   .WithCommentCount()
   .And()
   .WithCommentsHavingDateLessThan(selectedDate);


Post post = _repository.Find(query);

Однако в вашем конкретном случае я должен задаться вопросом о вашем дизайне. Вы говорите, что не можете загрузить комментарии с постом. Зачем? Вы просто слишком беспокоитесь о производительности? Это случай преждевременной оптимизации? (мне так кажется)

Если бы у меня был объект Post, это был бы мой совокупный корень, и он был бы с прикрепленными комментариями. И тогда все, что вы хотите сделать, будет работать в каждом сценарии.

1 голос
/ 16 марта 2009

Поскольку нам нужно было срочно решить проблему, которую я изложил в своем первоначальном вопросе, мы прибегли к следующему решению. Мы добавили коллекцию свойств (словарь) к каждому объекту модели, и, если это необходимо, DAL вставляет пользовательские данные в. Чтобы установить какой-то элемент управления, коллекция свойств основана на экземплярах назначенного класса, и она поддерживает только простые типы данных (целые числа, даты, ...), и это все, что нам нужно при движении и, скорее всего, когда-нибудь понадобится. , Типичный случай, который это решает: загрузка объекта со счетчиками для его подколлекций вместо полных заполненных коллекций. Я подозреваю, что это, вероятно, не получит никакой награды за разработку программного обеспечения, но это было самое простое и наиболее практичное решение для нашего случая.

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

Для этого у меня обычно есть RepositoryStatus и класс Status, который действует как мой объект передачи данных (DTO). Класс Status используется в моем слое службы приложений (по той же причине), от которого наследуется RepositoryStatus. Затем с помощью этого класса я могу возвращать сообщения об ошибках, объекты ответа и т. Д. Из уровня Repository. Этот класс является универсальным в том смысле, что он будет принимать любой объект и выводить его для получателя.

Вот класс статуса:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RanchBuddy.Core.Domain;
using StructureMap;

namespace RanchBuddy.Core.Services.Impl
{
    [Pluggable("Default")]
    public class Status : IStatus
    {
        public Status()
        {
            _messages = new List<string>();
            _violations = new List<RuleViolation>();
        }

        public enum StatusTypes
        {
            Success,
            Failure
        }

        private object _object;
        public T GetObject<T>()
        {
            return (T)_object;
        }
        public void SetObject<T>(T Object)
        {
            _object = Object;
        }

        private List<string> _messages;
        public void AddMessage(string Message)
        {
            _messages.Add(Message);
        }
        public List<string> GetMessages()
        {
            return _messages;
        }
        public void AddMessages(List<string> Messages)
        {
            _messages.AddRange(Messages);
        }

        private List<RuleViolation> _violations;
        public void AddRuleViolation(RuleViolation violation)
        {
            _violations.Add(violation);
        }
        public void AddRuleViolations(List<RuleViolation> violations)
        {
            _violations.AddRange(violations);
        }
        public List<RuleViolation> GetRuleViolations()
        {
            return _violations;
        }
        public StatusTypes StatusType { get; set; }
    }
}

А вот RepositoryStatus:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RanchBuddy.Core.Services.Impl;
using StructureMap;

namespace RanchBuddy.Core.DataAccess.Impl
{
    [Pluggable("DefaultRepositoryStatus")]
    public class RepositoryStatus : Status, IRepositoryStatus
    {

    }
}

Как вы можете видеть, RepositoryStatus пока не делает ничего особенного и просто использует утилиты объектов Status. Но я хотел зарезервировать за собой право продлить позже!

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

0 голосов
/ 01 апреля 2009

Если вы не привязаны к RDBM, то вам может пригодиться база данных, такая как CouchDB или Amazons SimpleDB. То, что вы описываете, тривиально в CouchDB View. Это, вероятно, на самом деле не отвечает на ваш конкретный вопрос, но иногда полезно взглянуть на радикально разные варианты.

0 голосов
/ 07 марта 2009

Не могу сказать, что я действительно вижу, в чем проблема, просто стреляя в воздух здесь:

  • Добавить конкретную сущность для инкапсуляции информации, которую вы хотите
  • Добавить объект недвижимости Комментарии к сообщению. (Я не понимаю, почему это потребует от вас получения всех комментариев - вы можете просто получить комментарии для конкретного загружаемого вами сообщения)
  • Используйте отложенную загрузку, чтобы получать комментарии только при доступе к свойству

Я думаю, что у вас будет больше шансов увидеть ответ на свой вопрос, если вы сделаете платформу, язык и O / R mapper специфичными (похоже, для .NET C # или VB, поскольку вы упомянули LINQ. LINQ 2 SQL? Entity Framework «Что-то еще?»

...