2009-03-07 18 views
5

Jaki jest zalecany sposób zwracania danych z repozytorium ad hoc (niestandardowe przypadek po przypadku), które nie pasują do żadnych encji modelowych lub które rozszerzają niektóre?Dane ad hoc i wzorzec repozytorium

Przykładem 101 byłaby wszechobecna aplikacja hello word: system blogów. Załóżmy, że chcesz załadować listę wpisów, w których wpis po wpisie zawiera dodatkowe informacje, które nie istnieją w jednostce Post. Powiedzmy, że jest to liczba komentarzy oraz data i czas ostatniego komentarza. Byłoby to bardzo trywialne, gdyby ktoś używał zwykłego starego SQL i odczytał dane bezpośrednio z bazy danych. Jak mam to zrobić optymalnie przy użyciu wzorca repozytoriów, jeśli nie mogę pozwolić sobie na załadowanie wszystkich kolekcji komentarzy do każdego postu i chcę to zrobić w jednym uderzeniu bazy danych? Czy istnieje jakaś powszechnie stosowana wzorzec dla tej sytuacji? Teraz wyobraź sobie, że masz umiarkowanie złożoną aplikację internetową, w której każda strona wymaga nieco innych niestandardowych danych, a ładowanie pełnych hierarchii nie jest możliwe (wydajność, wymagania dotyczące pamięci itp.).

Niektóre losowe pomysłów:

  1. dodać listę właściwości do każdego modelu, który mógłby być zaludnione przez dane niestandardowych.

  2. Podsumowuje elementy modelu w każdym przypadku i tworzy niestandardowe czytniki dla każdej podklasy.

  3. Używaj LINQ, komponuj zapytania ad hoc i czytaj anonimowe klasy.

Uwaga: Poprosiłem o numer similar question recently, ale wydawało mi się, że jest zbyt ogólny i nie przyciąga zbyt wiele uwagi.

Przykład:

oparciu o sugestie w odpowiedzi poniżej, dodaję bardziej konkretny przykład. Oto sytuacja starałem się opisać:

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 

} 

Jeśli nie robić nic dodatkowego i używać off ORM półki, doprowadzi to do n + 1 zapytań lub ewentualnie jedno ładowanie kwerendy wszystkie wpisy i komentarze . Ale optymalnie, chciałbym móc po prostu wykonać jeden SQL, który zwróciłby jeden wiersz dla każdego postu, włącznie z tytułem, treścią itd. Oraz liczbą komentarzy i ostatnią datą komentarza w tym samym. To jest banalne w SQL. Problem polega na tym, że moje repozytorium nie będzie w stanie odczytać i dopasować tego typu danych do modelu. Gdzie idą maksymalne terminy i liczby?

Nie pytam, jak to zrobić. Zawsze możesz to zrobić jakoś: dodaj dodatkowe metody do repozytorium, dodaj nowe klasy, specjalne byty, użyj LINQ itp., Ale domyślam się, że moje pytanie jest następujące. Jak to się dzieje, że schemat repozytorium i właściwy rozwój oparty na modelach są tak szeroko akceptowane, ale nie wydają się zajmować tym pozornie bardzo powszechnym i podstawowym przypadkiem.

Odpowiedz

0

nie mogę powiedzieć, że naprawdę zobaczyć, co jest problem, tylko wypalanie w powietrzu tutaj:

  • Dodaj konkretny podmiot do hermetyzacji informacje yo chcą
  • dodać obiekt Komentarze do Poczty.(Nie widzę powodu, dla którego wymagałoby to pobrania wszystkich komentarzy - można po prostu pobrać komentarze do konkretnego wpisu, który ładujesz).
  • Użyj leniwego ładowania, aby pobierać komentarze tylko podczas uzyskiwania dostępu do usługi

Wydaje mi się, że miałbyś większą szansę na odpowiedź na twoje pytanie, gdybyś zrobił specyficzne dla platformy, języka i odwzorowania O/R (wydaje się, że jest to .NET C# lub VB, ponieważ wspomniałeś o LINQ.) LINQ 2 SQL? ? Coś jeszcze?)

+0

Dziękuję za skierowanie mnie do tego. Dodałem prosty konkretny przykład i więcej wyjaśnień. –

1

Na to pytanie jest dużo. Czy potrzebujesz tych konkretnych danych do procedury raportowania? Jeśli tak, to właściwym rozwiązaniem jest uzyskanie oddzielnego dostępu do danych w celach raportowania. Spłaszczone bazy danych, widoki, itd.

Czy jest to potrzeba zapytania ad-hoc? Jeśli tak, Ayende ma post na ten problem. http://ayende.com/Blog/archive/2006/12/07/ComplexSearchingQueryingWithNHibernate.aspx

Używa obiektu "Finder". Używa NHibernate, więc zasadniczo to, co robi, tworzy oddzielne zapytanie.

Zrobiłem coś podobnego w przeszłości, tworząc obiekt zapytania, który mogę wypełnić przed przekazaniem go do repozytorium (jakiś purystyczny spekulant DDD sprzeciwi się temu, ale uważam, że jest elegancki i łatwy w użyciu).

Obiekt Query implementuje interfejs płynnie, więc mogę napisać to i uzyskać wyniki z powrotem:

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


Post post = _repository.Find(query); 

Jednak w konkretnym przypadku muszę się zastanawiać, w swojej konstrukcji. Mówisz, że nie możesz wczytać komentarzy za pomocą postu. Czemu? Czy jesteś po prostu zbyt zaniepokojony występem? Czy jest to przypadek przedwczesnej optymalizacji? (wydaje mi się, że to mi się podoba)

Gdybym miał obiekt Post, byłby to mój zagregowany katalog główny i byłby dołączony do komentarzy. A wtedy wszystko, co chcesz zrobić, działa w każdym scenariuszu.

+0

Dziękuję. Twoje sugestie wydają się być dobrym początkiem. Zastanawiam się, gdzie faktycznie przechowujesz komentarz? Z pewnością nie ma w nim osobnego elementu danych w podmiocie Post. –

+0

Jeśli chodzi o wydajność, przykład z postami na blogu był tylko przykładem. Prawdziwa aplikacja, którą mam na myśli, już działa i nie możemy sobie pozwolić na załadowanie całej kolekcji. –

+0

Prawdziwa domena problemu ma znaczenie. Niemożliwe jest, aby ktokolwiek stwierdził, czy masz wadę projektową w swojej domenie, co może doprowadzić do lepszego rozwiązania. Posty i komentarze to problem rozwiązujący, a pytania nie mają znaczenia kontekstowego. –

1

Ponieważ musieliśmy pilnie rozwiązać problem, który przedstawiłem w swoim pierwotnym pytaniu, skorzystaliśmy z następującego rozwiązania. Dodaliśmy kolekcję właściwości (słownik) do każdej jednostki modelu, a jeśli DAL tego potrzebuje, dostosowuje niestandardowe dane do. W celu ustanowienia pewnego rodzaju kontroli, kolekcja własności jest wpisywana przez instancje wyznaczonej klasy i obsługuje tylko proste typy danych (liczby całkowite, daty, ...), które są wszystkim, czego potrzebujemy w ruchu, i najprawdopodobniej będą kiedykolwiek potrzebować . Typowy przypadek, który rozwiązuje to: ładowanie jednostki zliczeń dla jej podkolekcji zamiast pełnych zaludnionych kolekcji. Podejrzewam, że prawdopodobnie nie otrzymałem żadnej nagrody za projekt oprogramowania, ale było to najprostsze i najbardziej praktyczne rozwiązanie dla naszej sprawy.

+0

Myślę, że inną opcją byłoby dołączanie nazwanych zapytań do encji i używanie ich w repozytoriach. Interesujące pytanie jednak, tak źle, że niewiele osób zdawało się to zaszkodzić. – wds

0

Jeśli nie jesteś zamknięty w RDBM, to baza danych takich jak CouchDB lub Amazons SimpleDB może być czymś na co popatrzeć. To, co opisujesz, jest banalne w widoku CouchDB. To prawdopodobnie nie odpowiada na konkretne pytanie, ale czasami dobrze jest spojrzeć na radykalnie różne opcje.

0

W tym celu generalnie mam status RepositoryStatus i status, który działa jak mój obiekt transferu danych (DTO). Klasa Status jest używana w mojej warstwie usługi aplikacji (z tego samego powodu), z której dziedziczy status RepositoryStatus. Następnie za pomocą tej klasy mogę zwracać komunikaty o błędach, obiekty odpowiedzi itp. Z poziomu repozytorium. Ta klasa jest ogólna, ponieważ akceptuje każdy obiekt i wyrzuca go dla odbiorcy.

Oto klasa Status:

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

A oto 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 
    { 

    } 
} 

Jak widać RepositoryStatus nie ma jeszcze zrobić coś specjalnego i po prostu opiera się na obiektach status narzędzia. Ale chciałem zarezerwować sobie prawo do przedłużenia w późniejszym terminie!

Jestem pewna, że ​​niektórzy z tamtejszych dieterów stwierdzą, że nie powinno się tego używać, jeśli ma się być kapłanem ... jednak wiem, że wasz ból w tym, że czasami trzeba wyjść poza coś więcej niż tylko zwrócony obiekt!

Powiązane problemy