Autor Beitrag
lightsaver
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 24


C# (VS 2008 Prof.)
BeitragVerfasst: Mi 18.06.08 16:04 
Hi,

in meinem Programm brauchte ich bisher immer nur Daten aus einer einzigen Tabelle, so dass ich bisher keine Probleme hatte.
Nun habe ich das erste mal das Problem, dass ich Daten aus 2 Tabellen benötige. Der Designer hat mir da eine Query erstellt mit einem InnerJoin und das funktioniert soweit auch gut, wenn ich mal nach dem Preview gehe, nur im Programm selber komme ich einfach nicht an die zusätzlichen Daten ran.

Hier mal Einzelheiten:

Ich habe ein typisiertes DataSet mit 4 Tabellen. In meinem Problemfall geht es um die Tabellen Mitteilungen und Kunden.
Bei Mitteilungen habe ich eine Query erstellt welche wie folgt aussieht (um einige Felder gekürzt):

ausblenden Quelltext
1:
2:
3:
4:
SELECT     mitteilungen.id, mitteilungen.empf, ... , kontakte.FirstName, kontakte.LastName, ...
FROM         mitteilungen INNER JOIN
                      kontakte ON mitteilungen.empf = kontakte.EntryID
WHERE     (mitteilungen.id = @id)


Hier sollte soweit alles stimmen, da ich beim Preview ja alle Spalten, die ich erwarte, auch bekomme.

Bisher habe ich meine Abfragen immer mit der Fill-Funktion, welche von mir aber noch einen Parameter bekommen hat, in ein DataSet (ich nenn es hier mal meinDataSet) geladen. Das habe ich hier auch probiert, allerdings mit GetData, da ich nicht so recht wusste, welche Tabelle ich angeben soll (kommen ja Daten aus 2 Tabellen).

Hier der Code:

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
public DataTable getMemoDS(long id)
        {
            meinDataSetTableAdapters.mitteilungenTableAdapter mitteilungentableadapter =
                        new prog.meinDataSetTableAdapters.mitteilungenTableAdapter();
            DataTable table = mitteilungentableadapter.GetDataByID(id);
            return table;
        }


Wie erwähnt wusste ich jetzt nicht, welche typisierte Tabelle ich angeben soll, da es ja eigentlich keine passende gibt. Daher habe ich mich für GetData entschieden und das Ergebnis in eine einfache DataTable laden lassen. Ich dachte nun, dass diese DataTable mir zugriff auf alle abgerufenen Spalten gibt, aber dem ist irgendwie nicht so.
table ist hier irgendwie vom Typ mitteilungenDataTable und somit komm ich, wenn ich bei Row reingehe auch nur diese Spalten. Die von Kontakte kann ich einfach nicht finden. Im Debugger habe ich mir table mal genauer angesehen und zumindest im RecordManager konnte ich alle Spalten mit Ergebnissen sehen, sonst aber nirgendwo.

Irgendwo muss ich was falsch machen oder einen kompletten Denkfehler haben, leider kann ich diesen aber nicht finden und bin auch bei meiner Suche in Tutorials und so nicht fündig geworden.
JüTho
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2021
Erhaltene Danke: 6

Win XP Prof
C# 2.0 (#D für NET 2.0, dazu Firebird); früher Delphi 5 und Delphi 2005 Pro
BeitragVerfasst: Mi 18.06.08 16:25 
Hallo,

ich möchte es als Denkfehler bezeichnen (auch wenn es das nicht wirklich ist). Die Daten, die Du mit einem Fill holst, werden in eine DataTable zusammengefasst. Wenn Du eine vorhandene Tabelle angibst, wird diese um entsprechende Felder erweitert; wenn Du einen anderen Tabellennamen angibst, gibt es eine zusätzliche Tabelle.

Du hast deshalb (mindestens) zwei Möglichkeiten:

Entweder Du verwendest die so erzeugte zusätzliche Tabelle. Das ist grundsätzlich kein Problem; aber Du kannst mit dieser Tabelle (eigentlich niemals oder nur auf Umwegen) neue Daten erzeugen oder welche ändern.

Oder Du verwendest nur DataTables, die den ursprünglichen Tabellen entsprechen. Dann benötigst Du für jede Tabelle und jedes Laden getrennte Select-Befehle (ggf. mit getrenntem DbDataAdapter bzw. TableAdapter), kannst aber mit DbDataAdapter.Update jegliche Art von Änderungen automatisch speichern. Die Verknüpfungen werden durch DataRelation dargestellt, die den ForeignKeys entsprechen.

Helfen diese Erläuterungen weiter? Jürgen
lightsaver Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 24


C# (VS 2008 Prof.)
BeitragVerfasst: Mi 18.06.08 21:54 
Danke! Also ich habe die zweite Variante jetzt mal umgesetzt und es funktioniert. Ich hatte zwar gehofft, dass ich es gerade nicht so umständlich machen muss, dafür eröffnet sich mir so eine andere Möglichkeit, bei der ich ansonsten später wahrscheinlich Probleme bekommen hätte.

Ach ja, eine Frage hat sich mir bei der Umsetzung noch eröffnet.

Ich lade mein DataSet ja über (jetzt zwei) TableAdapter. Mein Mitteilungsformular bekommt auf diese Weise halt per binding die ganzen Daten, also auch den Empfänger in die einzelnen Felder.

Ich habe nun in meinem Programm natürlich auch die Möglichkeit, die Kunden zu ändern, also z.B. die Adresse zu ändern. Da dies über andere Formulare läuft (hat mit den Mitteilungen ja auch nichts zu tun), läuft das über eigene DataSets. Wenn ich dies mache und die Änderung auch speicher, dann gehen die Daten natürlich in die Datenbank.
Nun die Frage:
Kann ich mein DataSet von den Mitteilungen da, ähnlich wie beim binding, aktuell halten, also jede Änderung in der DB sofort auch automatisch ins DataSet laden? Also eigentlich könnte die Frage auch lauten, ob mir der TableAdapter eine ähnliche Funktionalität bietet wie das Binding.
An sich wäre das sehr praktisch, da ich auf diese Weise sofort die Änderung in mein Formular bekomme, aber ich vermute mal ganz stark, dass dies nicht möglich sein wird, richtig?
JüTho
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2021
Erhaltene Danke: 6

Win XP Prof
C# 2.0 (#D für NET 2.0, dazu Firebird); früher Delphi 5 und Delphi 2005 Pro
BeitragVerfasst: Do 19.06.08 09:24 
Es ist auf jeden Fall möglich.

Variante 1: MS-SQL und vielleicht auch andere DBMS bieten DB-Ereignisse an. Dabei meldet sich ein Programm bei der DB an mit der Forderung, über Änderungen informiert zu werden; darüber bekommt ein anderes Programm (oder ein anderes Formular im gleichen Programm) entsprechende Informationen und kann durch ein erneutes Fill den aktuellen Zustand erhalten.

Variante 2 (schöner und allgemein nutzbar): Du erstellst ein DataSet außerhalb von Formularen; dieses DataSet enthält alle Tabellen, die in den Formularen benutzt werden, und alle Daten, die aktuell zur Verfügung stehen. Dann landen Änderungen des einen Formulars automatisch in der betreffenden DataTable und damit auch z.B. in einem anderen DataGridView.

Ein solches DataSet kann jedem Formular per Argument übergeben werden oder als Singleton-Klasse direkt aufgerufen werden.

Gruß Jürgen
lightsaver Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 24


C# (VS 2008 Prof.)
BeitragVerfasst: Do 19.06.08 16:17 
user profile iconJüTho hat folgendes geschrieben:
Es ist auf jeden Fall möglich.

Variante 1: MS-SQL und vielleicht auch andere DBMS bieten DB-Ereignisse an. Dabei meldet sich ein Programm bei der DB an mit der Forderung, über Änderungen informiert zu werden; darüber bekommt ein anderes Programm (oder ein anderes Formular im gleichen Programm) entsprechende Informationen und kann durch ein erneutes Fill den aktuellen Zustand erhalten.


Das wäre so etwa das, was ich mir vorgestellt habe, nur für MySql hab ich sowas bisher noch nicht gefunden. Gibt es da vielleicht bestimmte Fachbegriffe, nach denen ich suchen könnte?

Zitat:

Variante 2 (schöner und allgemein nutzbar): Du erstellst ein DataSet außerhalb von Formularen; dieses DataSet enthält alle Tabellen, die in den Formularen benutzt werden, und alle Daten, die aktuell zur Verfügung stehen. Dann landen Änderungen des einen Formulars automatisch in der betreffenden DataTable und damit auch z.B. in einem anderen DataGridView.

Ein solches DataSet kann jedem Formular per Argument übergeben werden oder als Singleton-Klasse direkt aufgerufen werden.


Diese Idee hatte ich auch schon, aber ich befürchte, dass ich mir so alle Daten aus der Datenbank in das Programm lade, was irgendwann, wenn das Programm dann mal so 2-3 Jahre in Benutzung war, doch recht viel werden könnte. Daher bin ich dann auf die einzelnen DataSets für jedes Formular umgestiegen. Vielleicht kommt mir da aber noch eine gute Idee, wie ich das globale DataSet nur auf die benötigten Daten beschränken kann.

Danke auf jeden Fall für die schnellen und guten Antworten
JüTho
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2021
Erhaltene Danke: 6

Win XP Prof
C# 2.0 (#D für NET 2.0, dazu Firebird); früher Delphi 5 und Delphi 2005 Pro
BeitragVerfasst: Do 19.06.08 17:29 
zu 1. Suche nach "Database Events" oder "Postback".

zu 2. Versuche, möglichst wenig Daten einzulesen. Es hilft nichts, immer alle Kunden (mit allen Adressen, Telefon usw.) bereitzuhalten. Vielmehr sollte es eine Auswahlmaske geben: Eingabe "Mü", dann gibt es alle Kunden, die mit diesem Namen anfangen (aber nur als Auswahlliste mit Name, PLZ, Straße u.ä.); erst nach genauer Festlegung gibt es alle Angaben zu diesem einen Kunden. Erst dann werden einer oder mehrere Aufträge dazu bereitgestellt. Und wenn ein Kunde abgeschlossen ist, kann er im DataSet wieder gelöscht werden; dann hält sich alles in engen Grenzen und läuft zügig.

Jürgen
lightsaver Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 24


C# (VS 2008 Prof.)
BeitragVerfasst: Mi 25.06.08 20:38 
So, nach einigen Tagen Pause wollte ich mich heute mal kurz an Variante 2 probieren. Dabei bin ich leider auf ein grundsätzliches Problem gestoßen, und wollte mal wissen, ob es noch eine bessere Variante gibt als meine.

Also, der Tip war ja, ein DataSet außerhalb der Formulare zu erstellen. Zuerst wollte ich das einfach in eine eigene Klasse packen. Da habe ich aber ein Problem: entweder ich übergebe die Instanz der Klasse wirklich an jede Instanz der einzelnen Formulare (also auch, wenn ein Formular das ganze vielleicht noch nicht benötigt, ein späteres aber schon), oder ich kann möglicherweise irgendwann mal nicht auf die Instanz zugreifen.

Um dieses Problem zu umgehen wollte ich eine systemweite Variable. Dafür habe ich nun das DataSet direkt im Hauptformular (also dem MDI-Parent) erstellt.
Meine Idee dahinter war, dass ich dieses Formular während der gesamten Laufzeit des Programmes ja nicht schließe (sonst ist das Programm ja beendet). Da nun jedes einzelne Formular ein Child von dem Hauptfenster ist, kann ich über (MainForm)this.MDIParent dann ja auf das DataSet zugreifen (zumindest sollte ich das können, habe das noch nicht ausprobiert, da Fußball jetzt gleich beginnt ;))

Die Frage ist nun, ob dieses Vorgehen sinnvoll ist oder ob es vielleicht sogar bessere Möglichkeiten gibt.
JüTho
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2021
Erhaltene Danke: 6

Win XP Prof
C# 2.0 (#D für NET 2.0, dazu Firebird); früher Delphi 5 und Delphi 2005 Pro
BeitragVerfasst: Do 26.06.08 08:59 
user profile iconlightsaver hat folgendes geschrieben:
Die Frage ist nun, ob dieses Vorgehen sinnvoll ist oder ob es vielleicht sogar bessere Möglichkeiten gibt.

Ich empfehle für solche Situationen immer (wie oben schon geschehen) eine Singleton-Klasse. Das ist das Grundmuster dazu:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
public sealed partial class MainContext
{
  #region Singleton, Konstruktor
  private static MainContext instance = null;
  
  public static MainContext Instance {
    get {
       //  beim erstmaligen Aufruf intern erzeugen
      if (instance == null)
        instance = new MainContext();
      return instance;    
    }
  }
  
  private MainContext()
  {
    //  interne Daten initialisieren
    vsActions = new MainActions();
  }
  #endregion
}

Beachte vor allem, was public bzw. private und was static ist. Der Aufruf erfolgt jederzeit und überall durch:
ausblenden C#-Quelltext
1:
MainContext context = MainContext.Instance;					

Dabei brauchst Du Dir keine Gedanken zu machen, ob, wann und wie diese Instanz erzeugt wurde. Das kannst Du selbst "irgendwo" steuern, ohne es immer zu berücksichtigen.

Ich weiß, dass andere Programmierer Singleton-Klassen nicht mögen (und es auch Gründe dagegen gibt); aber ich halte sie genau bei der Datenhaltung für äußerst praktisch.

Jürgen