none
Datenmodell -- Allgemein gehalten, LinQ einer 1:n m:n Verbindungen RRS feed

  • Frage

  • Hallo,
    ich würde gerne eine Allgemeinlösung bekommen.
    Ich habe ein Dataset mit Datatables  m:n  und 1:n Relations.
    Jetzt will ich in Abhängigkeit von der Position und dem Variablennamen
    eine Abfrage erzeugen, um in der Tabelle den Inhalt zu bekommen.
    Wie müßte ich das machen.
    Primär-/Fremdschlüssel
    Linqpad
    Mit Linqpad kann ich keine XML Datei laden.
    Die Daten halten sich so in Grenzen, dass eine Datenbank unsinnig wäre.
    Gibt es Möglichkeiten über so einen Art Wizard die XML Datei zu laden und dann so eine Art SQL Query zu erzeugen.

    Oder über die XSD Datei?

    Grüße Oliver

    Datenmodell

    var qryPass3 = (from a in A.AsEnumerable()

    join b in B.AsEnumerable() on  a.Id equals b.IdSideNumber
    from c in C.AsEnumerable()
    join d in D.AsEnumerable() on decimal.
    where (b.Active == false)
    select new
    {
     Element11 = a.Field<Int32>("Spaltenname A"),
     Element12 = b.Field<Int32>("Spaltenname A"),
     Element13 = b.Field<Int32>("Spaltenname B"),
     Element14 = b.Field<bool>("Active"),
    }).ToList();


    Mittwoch, 15. November 2017 17:29

Antworten

  • Hi Oliver,
    hier mal eine Konsolendemo, wie man die Aufgabe lösen kann:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace ConsoleApp1
    {
      class Program26
      {
        static void Main(string[] args)
        {
          try
          {
            Demo c = new Demo();
            c.Execute();
          }
          catch (Exception ex) { Console.WriteLine(ex.ToString()); }
          Console.WriteLine("Fertig, Abschluss mit beliebiger Taste");
          Console.ReadKey();
        }
    
        internal class Demo
        {
          internal void Execute()
          {
            // Demo-Daten generieren
            List<cDTA> DTA = new List<cDTA>();
            List<cDTB> DTB = new List<cDTB>();
            List<cDTC> DTC = new List<cDTC>();
            List<cDTD> DTD = new List<cDTD>();
            List<cDTBC> DTBC = new List<cDTBC>();
            for (int i = 1; i < 10; i++)
            {
              DTA.Add(new cDTA() { Id = i, Name = $"DTA {i}" });
              DTC.Add(new cDTC() { Id = i, Name = $"DTC {i}" });
              for (int k = 1; k < 10; k++)
              {
                DTB.Add(new cDTB() { Id = i, Name = $"DTB {i} {k}", Position = k, id_A = i });
                DTD.Add(new cDTD() { Id = i, Name = $"DTD {i} {k}", Inhalt = $"Inhalt {k}", id_C = i });
                for (int l = 1; l < 10; l++)
                {
                  DTBC.Add(new cDTBC() { Id = i, Name = $"DTBC {i} {k} {l}", id_B = k, id_C = i });
                }
              }
            }
            //
            var q = from b in DTB
                    where b.Position == 5 // in DTB nur Daten mit bestimmer Position
                    join bc1 in DTBC on b.Id equals bc1.id_B into bcg // Liste der Childs zu Master mit Position=5
                    from bc2 in bcg
                    join c in DTC on bc2.id_C equals c.Id into c2 // Liste der verknüpften DTC's
                    from c1 in c2
                    join d in DTD on c1.Id equals d.id_C // Verknüpfung zum Child DTD
                    where d.Name == "DTD 1 3" // dort nach Namen selektieren
                    select new { b = b.Name, bc2 = bc2.Name, c1 = c1.Name, inhalt = d.Inhalt }; // neues anonymes Ergebnis-Objekt
    
            foreach (var o in q)
            {
              Console.WriteLine($"{o.b} - {o.bc2} - {o.c1} - {o.inhalt}");
            }
          }
    
          /// <summary>
          /// Master von cDTB
          /// </summary>
          class cDTA
          {
            public int Id { get; set; }
            public string Name { get; set; }
          }
          /// <summary>
          /// Child von cDTA und Master zu cDTBC, d.h. n:m zu cDTC
          /// </summary>
          class cDTB
          {
            public int Id { get; set; }
            public string Name { get; set; }
            public int Position { get; set; }
            public int id_A { get; set; }
          }
          /// <summary>
          /// n:m zwischen cDTB und cDTC
          /// </summary>
          class cDTBC
          {
            public int Id { get; set; }
            public string Name { get; set; }
            public int id_B { get; set; }
            public int id_C { get; set; }
    
          }
          /// <summary>
          /// Master von cDTD und Master zu cDTBC, d.h. n:m zu cDTB
          /// </summary>
          class cDTC
          {
            public int Id { get; set; }
            public string Name { get; set; }
          }
          /// <summary>
          /// Child von cDTC
          /// </summary>
          class cDTD
          {
            public int Id { get; set; }
            public string Name { get; set; }
            public string Inhalt { get; set; }
            public int id_C { get; set; }
          }
        }
      }
    }


    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks

    Sonntag, 19. November 2017 21:00
  • Hi Oliver,
    die IntelliSense zeigt an der Stelle, wo jetzt into steht, nur 3 Möglichkeiten. Da hält sich der Aufwand, mal zu lesen, in Grenzen. Das Schöne an LinQ ist, dass man die Teilabfragen auch hintereinander separat ausführen kann und damit schrittweise die Lösung erstellen:

            var q1 = from b in DTB
                    where b.Position == 5 // in DTB nur Daten mit bestimmer Position
                    join bc1 in DTBC on b.Id equals bc1.id_B select bc1 // Liste der Childs zu Master mit Position=5
                    
    
            var q2 = from bc2 in q1
                    join c in DTC on bc2.id_C equals c.Id select c // Liste der verknüpften DTC's
    
    .. usw.


    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks


    Montag, 20. November 2017 12:02

Alle Antworten

  • Hallo Oliver,

    Wie ich Deinem Bild und Code entnehme, erfolgt die innere Verknüpfung zwischen A und B durch die Entsprechung von A.Id und B.IdSideNumber, aber wie wird die Verknüpfung zwischen C und D vollzogen? Da die vierte Zeile in Deinem Code unvollständig aussieht, würdest Du klarlegen, welche Datasetfelder in C und D bei der Verknüpfung entsprechen sollen und ob sie auch Datasetfeldern in A und/oder B gleich sein sollen?

    Gruß,
    Dimitar


    Bitte haben Sie Verständnis dafür, dass im Rahmen dieses Forums, welches auf dem Community-Prinzip „IT-Pros helfen IT-Pros“ beruht, kein technischer Support geleistet werden kann oder sonst welche garantierten Maßnahmen seitens Microsoft zugesichert werden können.

    Donnerstag, 16. November 2017 17:23
    Administrator
  • Hallo Dimitar,

    also, ich habe eine 1:n Verknüpfung Tabelle A zu B

            ich habe eine 1:n Verknüpfung Tabelle C zu D

           ich habe ein Mapping für die Tabelle BC mit Fremdschlüssel von A mit Fremdschlüssel von C

    C kann viele Elemente von D haben.

    D hat den Fremdschlüssel von C

    C (Id) D (IdFremdschlüssel_Von_C)

    D hat 'Variablenname', 'Inhalt' als Spaltennamen.

    Jetzt gibt der User die Position aus B vor, mit dem gewünschten Variablennamen von D. Dann soll einfach der Inhalt zugeliefert werden. Da ist die Frage, wie kann ich das erreichen, wie sieht die LinQ Abfrage aus, was muss ich beachten, durch die m:n Verknüpfung zwischen B und C ist es schwierig. Danke für Tipps.

    Grüße Oliver

    Donnerstag, 16. November 2017 17:54
  • Hi Oliver,
    ich habe den Thread mehrfach gelesen und verstehe trotzdem nicht Dein Datenmodell. Kannst Du nicht mal einfach nur die Tabellennamen mit den Feldnamen aufführen und die Beziehungen zwischen ihnen, z.B. so:

    TabA
      int ID - Primärschlüssel

    TabB
      int ID - Primärschlüssel
      int FK - Fremdschlüssel zu TabA.ID
      int Position - Feld, nach dem zu filtern ist


    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks

    Donnerstag, 16. November 2017 18:57
  • Hallo,
    anbei nun das korrekte Datenbankmodell.
    //TabA
    //  int ID -Primärschlüssel
    //TabB
    //  int ID -Primärschlüssel
    //  int FK -Fremdschlüssel zu TabA.ID
    //  int Position -Feld, nach dem zu filtern ist
    //TabC
    //  int ID -Primärschlüssel
    //  int FK -Fremdschlüssel zu TabD.ID
    //  int Index -Feld, nach dem zu filtern ist
    //TabD
    //  int ID -Primärschlüssel
    //  int FK -Fremdschlüssel zu TabC.ID
    //  string VariableName-Feld, nach dem zu filtern ist
    //  string Inhalt-Feld, das ausgegeben werden muss
    
    //Die Positionen können viele Indexe aufweisen
    //Die Indexe können aber auch zu unterschiedlichen Positionen gehören
    // Pos...Index
    //1     1
    //1     2
    //1     3
    //2     4
    //3     4
    //3     1

    Viele Grüße Oliver
    Freitag, 17. November 2017 16:10
  • Hi Oliver,
    dieses Modell hat aber nichts mit n:m zu tun. Da ist überall eine 1:n Master-Child-Beziehung:

    TabA
      TabB
        TabD
          TabC


    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks

    Freitag, 17. November 2017 17:04
  • Hallo Peter,

    //TabA
    //  int ID -Primärschlüssel
    //TabB
    //  int ID -Primärschlüssel
    //  int FK -Fremdschlüssel zu TabA.ID
    //  int Position -Feld, nach dem zu filtern ist
    //TabC
    //  int ID -Primärschlüssel
    //  int FK -Fremdschlüssel zu TabD.ID
    //  int Index -Feld, nach dem zu filtern ist
    //TabD
    //  int ID -Primärschlüssel
    //  int FK -Fremdschlüssel zu TabC.ID
    //  string VariableName-Feld, nach dem zu filtern ist
    //  string Inhalt-Feld, das ausgegeben werden muss
    
    //Die Positionen können viele Indexe aufweisen
    //Die Indexe können aber auch zu unterschiedlichen 
    Positionen gehören
    // Tab BC also 
    //  int ID -Primärschlüssel
    //  int ID_FK -Fremdschlüssel zu TabB.ID
    //  int ID_FK -Fremdschlüssel zu TabC.ID
    
    // Pos...Index
    //1     1
    //1     2
    //1     3
    //2     4
    //3     4
    //3     1

    Ups. Vergessen.

    Grüße Oliver



    Freitag, 17. November 2017 17:59
  • Hi Oliver,
    jetzt hast Du eine n:m Beziehung zwischen TabB und TabC. TabA ist Master zu TabB, TabD ist Master zu TabC.

    Die einzige Frage, die ich im Thread gefunden habe, war: Oder über die XSD Datei?

    Was erwartest Du für eine Antwort?

    Natürlich kann man dafür eine Schema-Datei (xsd) erstellen. Viel wichtiger ist: "Was willst Du erreichen?". Wie sieht die Aufgabenstellung aus?


    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks

    Freitag, 17. November 2017 18:13
  • Hallo Peter,

     

    >jetzt hast Du eine n:m Beziehung zwischen TabB und TabC.

    Ja!

    >TabA ist Master zu TabB,

    ja!

    TabD ist Master zu TabC.

    Nein!

    So TabC ist Master zu TabD

     

    >Jetzt gibt der User die Position aus B vor, mit dem gewünschten Variablennamen von D. Dann soll

    >einfach der Inhalt zugeliefert werden. Da ist die Frage, wie kann ich das erreichen, wie sieht die

    >LinQ Abfrage aus, was muss ich beachten, durch die m:n Verknüpfung zwischen B und C ist es schwierig.

     

    • D hat 'Variablenname''Inhalt' als Spaltennamen.

     

    Danke für Tipps.

     

    Viele Grüße Oliver

     

    Freitag, 17. November 2017 20:04
  • Hi Oliver,
    da nach Deiner Beschreibung zeigt der Fremdschlüssel in TabC auf den Primärschlüssel von TabD.

    //TabC
    // int ID -Primärschlüssel
    // int FK -Fremdschlüssel zu TabD.ID
    // int Index -Feld, nach dem zu filtern ist
    //TabD 

    Damit Ist TabD Master.


    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks

    Freitag, 17. November 2017 20:27
  • Jetzt stimmt's aber😉

    //TabA

    //  int ID -Primärschlüssel

    //TabB

    //  int ID -Primärschlüssel

    //  int FK -Fremdschlüssel zu TabA.ID

    //  int Position -Feld, nach dem zu filtern ist

    //TabC

    //  int ID -Primärschlüssel

    //  int Index -Feld, nach dem zu filtern ist

    //TabD

    //  int ID -Primärschlüssel

    //  int FK -Fremdschlüssel zu TabC.ID

    //  string VariableName-Feld, nach dem zu filtern ist

    //  string Inhalt-Feld, das ausgegeben werden muss

     

    //Die Positionen können viele Indexe aufweisen

    //Die Indexe können aber auch zu unterschiedlichen

    Positionen gehören

    // Tab BC also

    //  int ID -Primärschlüssel

    //  int ID_FK -Fremdschlüssel zu TabB.ID

    //  int ID_FK -Fremdschlüssel zu TabC.ID

     

    // Pos...Index

    //1     1

    //1     2

    //1     3

    //2     4

    //3     4

    //3     1

    Grüße Oliver

    Freitag, 17. November 2017 20:47
  • Hi Oliver,
    Bau erst einmal ein passendes Datenmodell und formuliere bitte dann Deine Fragen etwas detaillierter.

    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks

    Freitag, 17. November 2017 21:07

  • Bau erst einmal ein passendes Datenmodell und formuliere bitte dann Deine Fragen etwas detaillierter.


    Hallo Peter,
     m zu n
    ja auch hier hast wieder Recht, sauber aufzeichnen.
     
    Das Problem ist ja nicht das Befüllen. Da habe ich meine Id und ich kann diese als Fremdschlüssel der Child Tabelle zuweisen.
     
    Das Problem ist
      TabelleA  Name = Seite 1
      TabelleB Position = 5
     
    Befülle hier aus TabelleC    Name 4,7,8
     
    TabelleC hat ja 4,7,8 drin mit Child Tabelle D
                       Name = HezZaehler   Inhalt 0xA
                       Name = DezZaehler   Inhalt 0035235
                       Name = JulianDatum   Inhalt 2342017
     
    Ich möchte a)
      bestehende Positionen befüllen, ändern.
    b) Ich möchte eine Query erstellen
       Gib mir von Position 5, die Inhalte aus mit dem Name JulianDatum.
     
    Wie kann ich das korrekt umsetzen?
     
    Noch einen schönen Sonntag und Danke für Tipps.
     
    Viele Grüße Oliver
                       
     
           
           DataSetMzuN DSMzuN;
                DSMzuN = new DataSetMzuN();
    
                DataRow[] arrRows;
                foreach (DataRelation relation in DSMzuN.DTA.ChildRelations)
                {
                    if (relation.RelationName == "FK_DTA_DTB")
                    {
                        foreach (DataRow row in DSMzuN.DTB.Rows)
                        {
                            arrRows = row.GetChildRows(relation); // DataRowVersion.Proposed);
                                                                  // Print values of rows.
                            foreach (var item in arrRows)
                            {


    Sonntag, 19. November 2017 12:10
  • Hi Oliver,
    hier mal eine Konsolendemo, wie man die Aufgabe lösen kann:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace ConsoleApp1
    {
      class Program26
      {
        static void Main(string[] args)
        {
          try
          {
            Demo c = new Demo();
            c.Execute();
          }
          catch (Exception ex) { Console.WriteLine(ex.ToString()); }
          Console.WriteLine("Fertig, Abschluss mit beliebiger Taste");
          Console.ReadKey();
        }
    
        internal class Demo
        {
          internal void Execute()
          {
            // Demo-Daten generieren
            List<cDTA> DTA = new List<cDTA>();
            List<cDTB> DTB = new List<cDTB>();
            List<cDTC> DTC = new List<cDTC>();
            List<cDTD> DTD = new List<cDTD>();
            List<cDTBC> DTBC = new List<cDTBC>();
            for (int i = 1; i < 10; i++)
            {
              DTA.Add(new cDTA() { Id = i, Name = $"DTA {i}" });
              DTC.Add(new cDTC() { Id = i, Name = $"DTC {i}" });
              for (int k = 1; k < 10; k++)
              {
                DTB.Add(new cDTB() { Id = i, Name = $"DTB {i} {k}", Position = k, id_A = i });
                DTD.Add(new cDTD() { Id = i, Name = $"DTD {i} {k}", Inhalt = $"Inhalt {k}", id_C = i });
                for (int l = 1; l < 10; l++)
                {
                  DTBC.Add(new cDTBC() { Id = i, Name = $"DTBC {i} {k} {l}", id_B = k, id_C = i });
                }
              }
            }
            //
            var q = from b in DTB
                    where b.Position == 5 // in DTB nur Daten mit bestimmer Position
                    join bc1 in DTBC on b.Id equals bc1.id_B into bcg // Liste der Childs zu Master mit Position=5
                    from bc2 in bcg
                    join c in DTC on bc2.id_C equals c.Id into c2 // Liste der verknüpften DTC's
                    from c1 in c2
                    join d in DTD on c1.Id equals d.id_C // Verknüpfung zum Child DTD
                    where d.Name == "DTD 1 3" // dort nach Namen selektieren
                    select new { b = b.Name, bc2 = bc2.Name, c1 = c1.Name, inhalt = d.Inhalt }; // neues anonymes Ergebnis-Objekt
    
            foreach (var o in q)
            {
              Console.WriteLine($"{o.b} - {o.bc2} - {o.c1} - {o.inhalt}");
            }
          }
    
          /// <summary>
          /// Master von cDTB
          /// </summary>
          class cDTA
          {
            public int Id { get; set; }
            public string Name { get; set; }
          }
          /// <summary>
          /// Child von cDTA und Master zu cDTBC, d.h. n:m zu cDTC
          /// </summary>
          class cDTB
          {
            public int Id { get; set; }
            public string Name { get; set; }
            public int Position { get; set; }
            public int id_A { get; set; }
          }
          /// <summary>
          /// n:m zwischen cDTB und cDTC
          /// </summary>
          class cDTBC
          {
            public int Id { get; set; }
            public string Name { get; set; }
            public int id_B { get; set; }
            public int id_C { get; set; }
    
          }
          /// <summary>
          /// Master von cDTD und Master zu cDTBC, d.h. n:m zu cDTB
          /// </summary>
          class cDTC
          {
            public int Id { get; set; }
            public string Name { get; set; }
          }
          /// <summary>
          /// Child von cDTC
          /// </summary>
          class cDTD
          {
            public int Id { get; set; }
            public string Name { get; set; }
            public string Inhalt { get; set; }
            public int id_C { get; set; }
          }
        }
      }
    }


    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks

    Sonntag, 19. November 2017 21:00
  • Hallo Peter,

    sieht gut aus, ich teste es heute.

    Danke. Das into fehlte mir.

    Grüße Oliver

    Montag, 20. November 2017 07:01
  • Hi Oliver,
    wenn Dir das into fehlte, warum bemühst Du da nicht mal die Hilfe?

    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks

    Montag, 20. November 2017 08:48
  • Hallo Peter,

    also es passt. Danke.

    Ja, wenn ich wüsste, daß ich nach "into" googeln soll, wäre es möglich gewesen.😉

    Du bist ein Freund von Linq klassisch, ist lesbarer.

    Und schon noch uptodate, ja?

    Grüße Oliver

    Montag, 20. November 2017 11:42
  • Hi Oliver,
    die IntelliSense zeigt an der Stelle, wo jetzt into steht, nur 3 Möglichkeiten. Da hält sich der Aufwand, mal zu lesen, in Grenzen. Das Schöne an LinQ ist, dass man die Teilabfragen auch hintereinander separat ausführen kann und damit schrittweise die Lösung erstellen:

            var q1 = from b in DTB
                    where b.Position == 5 // in DTB nur Daten mit bestimmer Position
                    join bc1 in DTBC on b.Id equals bc1.id_B select bc1 // Liste der Childs zu Master mit Position=5
                    
    
            var q2 = from bc2 in q1
                    join c in DTC on bc2.id_C equals c.Id select c // Liste der verknüpften DTC's
    
    .. usw.


    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks


    Montag, 20. November 2017 12:02