2009-03-07 4 views
5

Was ist die empfohlene Methode, Ad-hoc-Daten (von Fall zu Fall) aus dem Repository zurückzugeben, die nicht in Modell-Entitäten passen oder welche erweitern?Ad-hoc-Daten und Repository-Muster

Das 101-Beispiel wäre die allgegenwärtige Hallo-Wort-Anwendung: ein Blog-System. Angenommen, Sie möchten eine Liste von Posts laden, bei denen der Post-Eintrag einige zusätzliche Informationen enthält, die in der Post-Entität nicht vorhanden sind. Nehmen wir an, es ist die Anzahl der Kommentare und das Datum und die Uhrzeit des letzten Kommentars. Dies wäre sehr trivial, wenn man das einfache alte SQL verwendet und Daten direkt aus der Datenbank liest. Wie soll ich es optimal mit dem Repository-Muster tun, wenn ich es mir nicht leisten kann, die gesamte Sammlung von Kommentaren für jeden Beitrag zu laden, und ich möchte es in einem Datenbank-Treffer tun? Gibt es ein gebräuchliches Muster für diese Situation? Stellen Sie sich nun vor, dass Sie eine relativ komplexe Webanwendung haben, bei der jede Seite ein wenig andere benutzerdefinierte Daten benötigt und das Laden vollständiger Hierarchien nicht möglich ist (Leistung, Speicheranforderungen usw.).

Einige zufällige Ideen:

  1. für jedes Modell eine Liste der Eigenschaften hinzufügen, die von den benutzerdefinierten Daten gefüllt werden könnten.

  2. Untergeordnete Modell-Entitäten von Fall zu Fall und benutzerdefinierte Leser für jede Unterklasse erstellen.

  3. Verwenden Sie LINQ, erstellen Sie Ad-hoc-Abfragen und lesen Sie anonyme Klassen.

Anmerkung: Ich habe eine similar question recently gefragt, aber es schien zu allgemein zu sein und die Besucher nicht viel Aufmerksamkeit.

Beispiel:

Basierend auf Anregungen in Antworten unten, ich bin ein konkreteres Beispiel hinzufügen. Hier ist die Situation war ich versucht, zu beschreiben:

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 

} 

Wenn ich tun nichts extra und verwenden eine aus dem Regal ORM, dies zu n + 1 Abfragen führen wird oder möglicherweise eine Abfrage Laden alle Beiträge und Kommentare . Aber am besten möchte ich in der Lage sein, nur eine SQL auszuführen, die eine Zeile für jeden Beitrag einschließlich des Posttitels, des Körpers usw. und der Anzahl der Kommentare und des letzten Kommentardatums in demselben zurückgibt. Dies ist in SQL trivial. Das Problem ist, dass mein Repository diese Art von Daten nicht in das Modell einlesen und anpassen kann. Wohin gehen die maximalen Daten und die Anzahl?

Ich frage nicht, wie das geht. Sie können es immer tun irgendwie: fügen Sie zusätzliche Methoden zum Repository hinzu, fügen Sie neue Klassen, spezielle Entitäten, verwenden Sie LINQ usw., aber ich denke, meine Frage ist die folgende. Wie kommt es, dass das Repository-Muster und die richtige modellgetriebene Entwicklung so weit akzeptiert sind, aber sie scheinen diesen scheinbar sehr häufigen und grundlegenden Fall nicht zu behandeln.

Antwort

0

Kann nicht sagen, dass ich wirklich sehen, was das Problem ist, nur in der Luft feuern hier:

  • eine bestimmte Einheit hinzufügen die Info yo wollen
  • Add-Eigenschaft Kommentare zum Post kapseln.(Ich verstehe nicht, warum Sie alle Kommentare abrufen müssen - Sie können einfach die Kommentare für den jeweiligen Beitrag abrufen)
  • Verwenden Sie Lazy Loading, um nur die Kommentare abzurufen, wenn Sie auf die Eigenschaft
zugreifen

Ich denke, Sie hätten eine größere Chance, Ihre Frage beantwortet zu sehen, wenn Sie Plattform, Sprache und O/R Mapper spezifisch machen würden (scheint .NET C# oder VB zu sein, da Sie LINQ. LINQ 2 SQL? Entity-Framework ? Etwas anderes?)

+0

Danke, dass Sie mich darauf hingewiesen haben. Ich habe ein einfaches konkretes Beispiel und mehr Erklärung hinzugefügt. –

1

Es gibt eine Menge zu dieser Frage. Benötigen Sie diese spezifischen Daten für ein Reporting-Verfahren? Wenn dies der Fall ist, besteht die richtige Lösung darin, einen separaten Datenzugriff für Berichtszwecke zu haben. Abgeflachte Datenbanken, Ansichten, ect.

Oder ist es eine Ad-hoc-Abfrage brauchen? Wenn ja, hat Ayende einen Beitrag zu diesem Problem. http://ayende.com/Blog/archive/2006/12/07/ComplexSearchingQueryingWithNHibernate.aspx

Er verwendet ein "Finder" -Objekt. Er verwendet NHibernate, also erstellt er im Wesentlichen eine eigenständige Abfrage.

Ich habe in der Vergangenheit etwas Ähnliches getan, indem ich ein Query-Objekt erstellt habe, das ich füllen kann, bevor ich es einem Repository übergebe (einige DDD-Puristen werden dagegen streiten, aber ich finde es elegant und einfach zu benutzen).

Das Query-Objekt implementiert eine fließend-Schnittstelle, so kann ich dies schreibe und die Ergebnisse zurück:

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


Post post = _repository.Find(query); 

jedoch in Ihrem speziellen Fall muß ich an Ihrem Design wundern. Sie sagen, dass Sie die Kommentare nicht mit dem Beitrag laden können. Warum? Machst du dir nur Sorgen über die Leistung? Ist dies eine vorzeitige Optimierung? (scheint mir so)

Wenn ich ein Post-Objekt hätte, wäre es mein Aggregat root und es würde mit den Kommentaren kommen. Und dann würde alles, was Sie tun möchten, in jedem Szenario funktionieren.

+0

Danke. Ihre Vorschläge scheinen ein guter Anfang zu sein. Ich frage mich, wo Sie die Kommentare tatsächlich speichern? Sicherlich gibt es in der Post-Entität kein separates Datenelement dafür. –

+0

In Bezug auf die Leistung war das Beispiel mit den Blogposts nur ein Beispiel. Die eigentliche Anwendung, die mir vorschwebt, läuft bereits, und wir können es uns nicht leisten, die gesamten Sammlungen zu laden. –

+0

Die echte Problemdomäne macht einen Unterschied. Es ist unmöglich für irgendjemanden zu sagen, wenn Sie einen Designfehler mit Ihrer Domain haben, was tatsächlich zu einer besseren Lösung führen könnte. Posts & Comments ist ein Lösungsproblem und Ihre Fragen sind nicht sinnvoll. –

1

Da wir dringend das Problem lösen mussten, das ich in meiner ursprünglichen Frage umrissen hatte, griffen wir auf die folgende Lösung zurück. Wir haben jeder Modelleinheit eine Eigenschaftssammlung (ein Wörterbuch) hinzugefügt, und wenn die DAL dies benötigt, werden benutzerdefinierte Daten an sie angehängt. Um eine Art von Kontrolle zu etablieren, wird die Eigenschaftensammlung durch Instanzen einer bestimmten Klasse kodiert und unterstützt nur einfache Datentypen (Ganzzahlen, Daten, ...), was alles ist, was wir bei der Bewegung brauchen, und die meisten werden wahrscheinlich jemals brauchen . Ein typischer Fall, den dies löst, ist: Laden einer Entität mit Zählungen für ihre Untersammlungen anstelle von vollständig aufgefüllten Sammlungen. Ich vermute, dass dies wahrscheinlich keine Auszeichnung für ein Software-Design bekommen wird, aber es war die einfachste und praktischste Lösung für unseren Fall.

+0

Ich denke, die andere Option hätte einige benannte Abfragen an die Entitäten angehängt und diese in den Repositories verwendet. Interessante Frage, schade, dass so wenige Leute es zu knacken schienen. – wds

0

Wenn Sie nicht in einem RDBMs gesperrt sind, dann könnte eine Datenbank wie CouchDB oder Amazons SimpleDB etwas zu betrachten sein. Was Sie beschreiben, ist in einer CouchDB-Ansicht trivial. Dies beantwortet wahrscheinlich nicht wirklich Ihre spezifische Frage, aber manchmal ist es gut, sich radikal andere Optionen anzusehen.

0

Dafür habe ich in der Regel eine RepositoryStatus und eine Status-Klasse, die als mein Data Transfer Object (DTO) fungiert. Die Status-Klasse wird in meinem Anwendungsdienst-Layer (aus dem gleichen Grund) verwendet, von dem der RepositoryStatus erbt. Dann kann ich mit dieser Klasse Fehlermeldungen, Antwortobjekte usw. von der Repository-Schicht zurückgeben. Diese Klasse ist generisch, da sie jedes Objekt akzeptiert und für den Empfänger ausgibt.Hier

ist die Statusklasse:

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

Und hier ist der 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 
    { 

    } 
} 

Wie Sie sehen können die RepositoryStatus hat noch nichts Besonderes tun und verlässt sich nur auf die Statusobjekte Dienstprogramme. Ich wollte mir aber das Recht vorbehalten zu einem späteren Zeitpunkt zu verlängern!

Ich bin sicher, dass einige der hartgesottenen da draußen sagen werden, dass dies nicht verwendet werden sollte, wenn Sie ein prüde sein sollen ... aber ich kenne Ihren Schmerz darin manchmal müssen Sie mehr als nur ohnmächtig werden ein zurückgegebenes Objekt!

Verwandte Themen