Laden...

DataGridView über 2 Tabellen mit Zurückschreiben in eine der Tabellen

Erstellt von Cornflake vor 8 Jahren Letzter Beitrag vor 8 Jahren 1.230 Views
C
Cornflake Themenstarter:in
142 Beiträge seit 2007
vor 8 Jahren
DataGridView über 2 Tabellen mit Zurückschreiben in eine der Tabellen

verwendetes Datenbanksystem: DataSet, VS2008

Hallo Leute

In einem DataGridView will ich Inhalte aus zwei gejointen Tabellen darstellen. Die Inhalte kommen aus zwei CSV Dateien. In der einen CSV Datei kommen bei wiederholtem Einlesen immer mehr Datensätze dazu. In der anderen CSV Datei werden geprüfte Datensätze als abgehakt zurück geschrieben. Das einlesen klappt. aber das richtig darstellen, bzw. zurückschreiben klappt noch nicht, bzw. da stehe ich auf dem Schlauch.

Stellt euch vor, jemand schickt euch in regelmäßigen Abständen (alle 5 Minuten) eine anwachsende CSV-Datei in der Wegpunkte und Spielteilnehmer stehen. Diese Angaben prüft ihr und hakt sie ab, wenn ihr einen Wegpunkt als abgeschlossen anseht. Das ganze soll in einer DataGridView angezeigt werden.


# Tabellen:
TabWegpunkte  (WegpunktNr, Spielername, Zeitpunkt, Bemerkungen)
TabGeprüft       (WegpunktNr, Geprüft)

# DataRelation:
TabWegpunkt.WegpunktNr (n) zu (1) TabGeprüft.WegpunktNr

# DataGridView Anzeige entspricht SQL:
select
   TabGeprüft.Geprüft , 
   TabWegpunkte.WegpunktNr, 
   group_concat(TabWegpunkte.Spielername) as 'Spielername', 
   group_concat(TabWegpunkte.Zeitpunkt) as 'Zeitpunkt', 
   group_concat(TabWegpunkte.Bemerkungen) as 'Bemerkung'
from
   TabWegpunkt
join 
  TabGeprüft
   on TabWegpunkt.WegpunktNr = TabGeprüft.WegpunktNr
group by
   TabWegpunkt.WegpunktNr

# Hinweis: group_concat soll alle Spielernamen, etc. in der 
Gruppe des Wegpunktes zusammenfassen und sie in einer Zelle darstellen.

Das Problem:
Wenn jetzt in der Spalte mit der Checkbox ein Wegpunkt abgehakt wird, soll dass in die Tabelle TabGeprüft zurück geschrieben und in der CSV Datei gespeichert werden. Daher diese Tabelle erweitert sich damit um neue Einträge und bestehende Einträge werden geändert.
Nur wie bekomme ich dass hin? Zum Einen zwei Tabellen joinen als select und zum Anderen nur in einer inserten bzw. updaten?

Aktuell joinen ich per foreach die beiden Tabellen und stelle Sie als Datasource dem DataGridView zur Verfügung. Nur wenn jetzt ein Häckchen gesetzt wird, steht das zwar in der Jointabelle, aber ich muss das ja irgendwie in der CSV Datei zurückschreiben? X(

Ich habe schon überlegt mit einem TableAdapter zu arbeiten. nur der verbindet sich mit Datenbanken und nicht mit DataTables. Und per Code kann ich den auch nicht aufbauen.

Habt ihr eine Idee ?(

Viele Grüße Cornflake

5.658 Beiträge seit 2006
vor 8 Jahren

Hi Cornflake,

dein Programm besteht wie die meisten anderen Programme aus Eingabe, Verarbeitung, Anzeige und Ausgabe von Daten. Wenn du die einzelnen Bereiche trennst, wie du es ja in .Net allgemeine Projektstruktur MVC bereits angedeutet hast, dann solltest du jedes dieser Teilprobleme einzeln und unabhängig von den anderen lösen können. Siehe dazu nocheinmal: [Artikel] Drei-Schichten-Architektur.

Christian

Weeks of programming can save you hours of planning

C
Cornflake Themenstarter:in
142 Beiträge seit 2007
vor 8 Jahren

Hallo Christian
Prinzipiell haste ja recht, dass die 3 Schichten alles einfacher machen und ich habe meine Solution auch so aufgebaut, aber dennoch hilft das hier mir nicht weiter.

Mein Button in der Präsischicht ruft die Logikschicht auf. diese soll aus der Datenschicht die DataTables holen, damit in der Logikschicht diese entsprechend aufbereitet werden (join, sort, filter) um die Ergebnis-Datatable der Präsischicht über ein Ereignisrückmeldung zurückzuliefern. Die Präsischicht abboniert das Ereignis, JoinDatatableIstFertig.
Wenn ein Haken gesetzt wird, würde ich jetzt der Logikschicht melden, dass in Zeile ein Haken gesetzt wurde oder nicht und diese soll dass dann in der Datenschicht durchführen.

Leider weiß ich noch nicht genau, wie ich das umsetze. Jedoch habe ich wegen joinen der Datatables jetzt rausgefunden, dass Linq das können soll.

Dabei steht wieder ein Problem im Raum.
Wenn ich z.B.


var result = from tw in TabWegpunkte.AsEnumerable()
                              join tg in TabGeprüft.AsEnumerable()
                              on tw.Field<string>("WegpunktNr") equals tg.Field<string>("WegpunktNr")
                              select new
                              {
                                  Geprüft = tg.Field<string>("Geprüft"),
                                  WegpunktNr = tw.Field<string>("WegpunktNr"),
                                  Spielername = tw.Field<string>("Spielername"),
                                  Zeitpunkt = tw.Field<string>("Zeitpunkt"),
                                  Bemerkung = tw.Field<string>("Bemerkung")
                              };

                //Group by habe ich noch nicht hinbekommen, aber selbst bei join geht CopyToDataTable() nicht
              
  DataTable TabErgebnis = result.CopyToDataTable(); //Geht nicht, da anscheinend keine DataRow

Laut Microsofts eigener Seite müsste es nach dem "Example" 2 unter "Creating a Custom CopyToDataTable<T> Method" funktionieren, aber es klappt nicht.
MSDN Creating a DataTable From a Query (LINQ to DataSet) .Net 3.5

Wie komme ich hier weiter? Oder hat jemand von euch noch einen ganz anderen Ansatz das ursprüngliche Problem zu lösen?

Viele Grüße
Cornflake

771 Beiträge seit 2009
vor 8 Jahren

Hast du denn die CopyToDataTable-Methode auch übernommen, so wie in How to: Implement CopyToDataTable<T> Where the Generic Type T Is Not a DataRow beschrieben?

C
Cornflake Themenstarter:in
142 Beiträge seit 2007
vor 8 Jahren

Hi
Habe den Eintrag gesehen, aber verstehe ihn nicht wirklich.
Wenn ich das richtig sehe, bezieht sich der auf ein Dictionary Objekt, dass über IEnumerable um eine DataRow Rückgabe erweitert wird. Da ich aber schon DataTables als Vorlage habe, weiß ich nicht, wie ich das von Dictionary übertragen kann. Wenn ich das richtig sehe würde es aufgrund des "select new" im LINQ da auch nicht anwendbar sein.

771 Beiträge seit 2009
vor 8 Jahren

Übernimm einfach den Source der beiden Klassen ObjectShredder und CustomLINQtoDataSetMethods in dein Projekt und rufe die Methode CopyToDataTable() dann auf - und wunder dich (dass es funktioniert 😄).
LINQ is magic!

C
Cornflake Themenstarter:in
142 Beiträge seit 2007
vor 8 Jahren

Habe eine Hilfsmethode (siehe unten) gefunden. Damit klappt das fast "generell" für alle LINQ Abfragen.

Es funktioniert, wenn ich ein
LINQ mit ... select new { <Spaltenangaben> };
schreibe, da es hier kein CopyToDataTable() gibt.

Allerdings bei einem
LINQ mit ... select <Tabelle>;
geht das nicht, da PropertyInfo nicht den Inhalt, sondern die DataTable Struktur zurück gibt und da scheint es mehrmals eine "Item" Spalte zu geben.
Dafür gibts an dieser Stelle wieder ein CopyToDataTable() und ich komme im Prinzip hin.
Jetzt würde ich gerne, die Methode um eine Erkennung für CopyToDataTable() erweitern, damit wenn diese Möglichkeit vorhanden ist, dieser Weg genommen wird. Leider weiß ich nicht wie ich das hier einbauen kann. Siehe Code Kommentar.


public static DataTable ConvertToDataTable<T>(IEnumerable<T> varlist)
        {
            //!! Hier weiß ich nicht wie auf auf CopyToDataTable prüfen kann.
            //if (varlist.Contains(CopyToDataTable))
            //    return varlist.CopyToDataTable(); 

            DataTable dtReturn = new DataTable();

            // column names 
            System.Reflection.PropertyInfo[] oProps = null;

            if (varlist == null) return dtReturn;

            foreach (T rec in varlist)
            {
                // Use reflection to get property names, to create table, Only first time, others will follow 
                if (oProps == null)
                {
                    oProps = ((Type)rec.GetType()).GetProperties();
                    foreach (System.Reflection.PropertyInfo pi in oProps)
                    {
                        Type colType = pi.PropertyType;

                        if ((colType.IsGenericType) && (colType.GetGenericTypeDefinition() == typeof(Nullable<>)))
                        {
                            colType = colType.GetGenericArguments()[0];
                        }

                        dtReturn.Columns.Add(new DataColumn(pi.Name, colType));
                    }
                }

                DataRow dr = dtReturn.NewRow();

                foreach (System.Reflection.PropertyInfo pi in oProps)
                {
                    dr[pi.Name] = pi.GetValue(rec, null) == null ? DBNull.Value : pi.GetValue
                    (rec, null);
                }

                dtReturn.Rows.Add(dr);
            }
            return dtReturn;
        }

5.658 Beiträge seit 2006
vor 8 Jahren

Hi Cornflake,

tut mir echt leid, das so sagen zu müssen, aber das ist pures Gefrickel. Du sagst ja selbst, daß du das "nicht wirklich" verstehst. Das Problem scheint mir nach wie vor zu sein, daß du alles in einem Schritt lösen willst, anstatt die Probleme der Reihe nach abzuarbeiten.

Für deine Anwendung benötigst du Funktionen zum Lesen und Schreiben von CSV-Dateien. Du benötigst ein Objektmodell, das den Einträgen der CSV-Dateien entspricht. Und du brauchst eine Funktion zum Markieren der Wegpunkte. Und ganz zum Schluß willst du die Daten auch in einer geeigneten Form auf dem Bildschirm anzeigen.

Diese Problemstellungen kann man jeweils einzeln abarbeiten, testen und debuggen und in einer Anwendung dann in der geeigneten Form zusammenführen.

Dafür benötigt man weder Linq noch Reflection, sondern es erfordert lediglich den Einsatz von Objektorientierter Programmierung und etwas Entwicklungsarbeit (deswegen heißt der Job auch "Entwickler").

Christian

Weeks of programming can save you hours of planning