Ideale Datenstruktur / -techniken zum Speichern generischer Scheduler-Daten in C #

8

Ich versuche, ein generisches Scheduler-Objekt in C # 4 zu implementieren, das eine Tabelle in HTML ausgibt. Grundlegendes Ziel ist es, ein Objekt zusammen mit verschiedenen Attributen anzuzeigen und festzustellen, ob es in einem bestimmten Zeitraum etwas getan hat.

Der Scheduler gibt eine Tabelle mit den Headern aus:

Detail Field 1 ....N| Date1.........N

Ich möchte die Tabelle mit einem Start- und einem Enddatum initialisieren, um den Datumsbereich zu erstellen (idealerweise können auch andere Zeiträume ausgeführt werden, z. B. Stunden, aber das ist nicht wichtig). Ich möchte dann ein generisches Objekt bereitstellen, dem Ereignisse zugeordnet sind.

Wenn ein Objekt Ereignisse innerhalb des Zeitraums hat, möchte ich, dass eine Tabellenzelle markiert wird

Z.B

Name Height Weight 1/1/2011 2/1/2011 3/1/20011...... 31/1/2011
Ben  5.11    75       X        X                       X
Bill 5.7     83                X        X

Also habe ich einen Scheduler mit Startdatum = 1/1/2011 und Enddatum 31/1/2011 erstellt

Ich möchte ihm mein Personenobjekt (bereits sortiert) geben und ihm mitteilen, welche Felder angezeigt werden sollen (Name, Größe, Gewicht).

Jede Person hat Ereignisse, die ein Start- und ein Enddatum haben. Einige Ereignisse beginnen und enden mit, aber sie sollten immer noch am relevanten Datum usw. angezeigt werden.

Idealerweise hätte ich es auch gerne mit einem Klassenbuchungsobjekt versehen können. Also versuche ich es generisch zu halten.

Ich habe ähnliche Javasript-Implementierungen usw. gesehen.

Was wäre eine gute Datenstruktur dafür? Irgendwelche Gedanken zu Techniken, mit denen ich es generisch machen könnte. Ich bin nicht großartig mit Generika, daher sind alle Tipps willkommen.

GraemeMiller
quelle
Bei Generika benötigen Sie noch eine Basisklasse oder Schnittstelle, um sie typsicher zu machen. Sie können Reflection verwenden, um die Eigenschaften zur Laufzeit
abzurufen
Ja, ich hatte erwartet, dass die Basisklasse der Datenstruktur eine Art Struktur enthält, die Seitenspaltenüberschriften enthält (die 1 ... bis n sein können), eine Art Struktur, die Hauptspaltenüberschriften enthält (die so groß wie die Zeit sind) Punkt) und dann Strukturen, die die tatsächlichen Zeilen enthalten. Der generische Teil war die Fähigkeit, ein zufälliges Objekt zu übergeben und zu definieren, welche ich dann die Spalten definieren würde, für die ich usw. benötigte.
GraemeMiller
Haben Sie schon einmal darüber nachgedacht, das Schlüsselwort "dynamic" für diese Aufgabe zu verwenden?
Dimi Takis
1
Diese interessante Diskussion über das Entwerfen von Daten kann Ihnen eine Richtung / Inspiration geben.
TheSilverBullet
Wenn Sie keine tatsächlichen Operationen an den Felddaten (Größe, Gewicht usw.) ausführen müssen, können Sie die in dieser Antwort zum Stapelüberlauf beschriebene Reflexion verwenden , um die Eigenschaftswerte und -namen abzurufen, die vom Objekt angezeigt werden sollen.
Nemec

Antworten:

2

Ich denke nicht, dass das zu hart ist. Was vermisse ich?

using System;
using System.Collections.Generic;

public class ScheduledPerson {
    public string Name {get; set;}

    public Dictionary<string, Object> Fields {
        get { return _fields; }
        set { _fields = value; }
    }
    private Dictionary<string, Object> _fields = new Dictionary<string, Object>();

    public List<Tuple<DateTime, DateTime>> Events {
        get { return _events; }
        set { _events = value; }
    }
    private List<Tuple<DateTime, DateTime>> _events = new List<Tuple<DateTime, DateTime>>();

    public ScheduledPerson(string name){
        Name = name;
    }
}

Ich sehe keine gute Möglichkeit, unsere ScheduledPerson-Klassen generisch zu gestalten, da die Felder anscheinend alles sein können. Ich speichere die Feldwerte als Objekte, da ich nichts sehe, was erfordert, dass sie dynamisch sind. Stellen Sie einfach sicher, dass alle Feldwerttypen eine sinnvolle ToString () - Implementierung haben.

Wenn Sie möchten, dass Ereignisse eine Liste von DateRange oder Ihrer eigenen Ereignisklasse anstelle von Tupel sind, können Sie dies gerne tun .

Anschließend müssen Sie eine separate Klasse schreiben, um jede ScheduledPerson in einer Tabelle zu rendern und alle Header aus allen ScheduledPerson-Datensätzen zu ermitteln. Wenn Sie mit zehntausend Personen zu tun haben, möchten Sie eine bessere Lösung, bei der alle Header gespeichert sind. Für die meisten Anwendungen ist es jedoch nicht schlecht, alle Felder aller ScheduledPersons aufzulisten, um die Header zu ermitteln.

Patrick Szalapski
quelle
0

Im Allgemeinen würde ich die Daten für Ihre Lösung nicht so speichern, wie Sie sie anzeigen möchten. Das führt zu sehr spezifischen Lösungen, die es schwierig machen, wenn sich Ihre Bedürfnisse ändern. Ich würde die Dinge in Bezug auf Entitäten in Ihrer Lösung aufschlüsseln und dann eine Reihe von LINQ-Abfragen erstellen, die Ihre Anzeigedaten generieren, wenn es Zeit ist, den Bericht zu generieren.

Der gesamte folgende Code ist strukturell und ohne Initialisierung usw.

public class Person
{
   public string Name {get;set;}
   public double Height {get;set;}
   public double Weight {get;set;}
}

public class ScheduledEvent
{
   public Person Attendee {get;set;}
   public DateTime EventDate {get;set;}
}

public class Schedule
{
   public DateTime StartDate {get;set; }
   public DateTIme EndDate {get;set;}
   List<Person> Persons {get;set;}
   List<ScheduledEvent> SheduledEvents {get;set;}
}

In der Praxis würde ich diese in einer Datenbank speichern und alle Abfragen zur Laufzeit mit etwas wie NHibernate oder Entity Framework ausführen. Für In-Memory-Demozwecke lauten Abfragen zum Erstellen Ihrer Tabellenzeilen jedoch wie folgt:

class TableRow
{
   string Name { get;set; }
   string Height {get;set; }
   string Weight {get;set; }
   List<string> DatesWithEvents {get; }
}

var columns = List<string>{ "Name", "Height", "Weight", }.Concat(
                schedule.ScheduledEvents
                  .Select(e=> e.EventDate.ToShortDate())
                  .Distinct())
              .ToList();
var rows = new List<TableRow>();

foreach(var p in schedule.Persons) 
{
   var row = new TableRow();
   row.Name = p.Name;
   row.Height = p.Height;
   row.Weight = p.Weight;
   row.DatesWithEvents = schedule.ScheduledEvents
     .Where(e => e.Person == p)
     .Select(e => e.EventDate.ToShortDate()).Distinct().ToList();
   rows.Add(row);
}    

Ordnen Sie dann in Ihrem Renderer einfach die Spalten zu, um zu wissen, wo das 'X' angebracht werden muss, um zu markieren, dass das Datum gefüllt ist. Mit mehr Aufwand können Sie dies in eine viel elegantere Lösung umgestalten, aber dies sind die Grundlagen.

Steve Mitcham
quelle