none
Abfrage verknüpfter Tabellen RRS feed

  • Frage


  • Hallo Leute,

    ich verwende 3 Tabellen welche miteinander verknüft sind.
    Ein [Produkt] besteht aus mehreren [Artikel], welche über [ProduktArtikel] verknüpft sind.

    [Artikel]
    ID
    ArtikelNr
    ...
    
    [Produkt]
    ID
    ProduktNr
    Bezeichnung
    ..
    
    [ProduktArtikel]
    ID
    ProduktId
    ArtikelId
    Menge
    ..


    Folgende Controller habe ich u.a. mittels "MVC-Controller mit Ansichten unter Verwendung von Entity Framework" erstellt:
    - ProduktController
    - ArtikelController
    - ProduktArtikelController

    Die entsprechenden Views (Index, Edit, Details, Delete, Create) habe ich ebenfalls.

    In https://localhost/xxxxx:/ProduktArtikel/Index werden mir sämtliche Verknüpfungen zwischen Produkt und Artikel richtig angezeigt.
    Ich kann die Werte der einzelnen Tabellen anzeigen.

    @Html.DisplayFor(modelItem => item.Produkt.ProduktNr)
    @Html.DisplayFor(modelItem => item.Produkt.Bezeichnung)
    @Html.DisplayFor(modelItem => item.Menge)
    @Html.DisplayFor(modelItem => item.Artikel.ArtikelNr)


    Soweit so gut.

    Jetzt möchte ich in https://localhost/xxxxx:/Produkt/Details das Produkt, sowie die enthaltenen Artikel anzeigen.
    Dazu liste ich mir zuerst die Produktdaten auf und habe in einer Tabelle folgenden Code zusätzlich eingefügt:

    @foreach (var item in Model.ProduktArtikel.OrderBy(s => s.Sort))
    

    { .. @Html.DisplayFor(modelItem => item.Produkt.ProduktNr) @Html.DisplayFor(modelItem => item.Produkt.Bezeichnung) @Html.DisplayFor(modelItem => item.Menge) @Html.DisplayFor(modelItem => item.Artikel.ArtikelNr) .. }

    Wie ihr bereits geahnt habt, funktioniert das leider nicht.
    Es werden zwar alle verknüpften Elemnte angezeigt, jedoch kann ich nicht auf die einzelnen Werte aus [Produkt] und [Artikel] zugreifen.
    D.h. "item.Produkt.xx" bzw. "item.Artikel.xx" liefert mir keine Werte.

    Hoffe ihr könnt mir da weiterhelfen.

    Visual Studio 2017
    .NET Core 2.0

    BG Paul





    Dienstag, 14. Mai 2019 10:19

Alle Antworten

  • Ganz oben zeigst du, daß [ProduktArtikel] eine Property _ProduktId_ hat. Es hat aber keine Property _Produkt_. Das Gleiche gilt analog für _ArtikelId_/_Artikel_

    Was du willst, ist natürlich klar. Was du im Moment tust, ist im Prinzip die Entitäten (der Datenbank) 1:1 als Objekte/Klassen nach außen zu liefern. Hier wäre es eventuell besser, eine etwas komplexere neue Entität zu liefern:

    [ProduktArtikelNeu]
    ID
    Produkt (vom Typ ProduktNeu)
    Artikel (vom Typ ArtikelNeu)
    Menge
    ...

    ProduktNeu / ArtikelNeu haben dann den Aufbau wie Produkt respektive Artikel. Das befüllen dieser Entität(en) musst du natürlich selbst übernehmen, aber dann kannst du so hierarchisch darauf zugreifen, wie im unteren Teil deines Codes gezeigt.

    Dienstag, 14. Mai 2019 11:40

  • Die Geister die ich rief ...
    Jetzt ist genau das passiert was ich befürchtet habe.
    Eine Antwort die ich nicht verstehe :-)

    Kannst du mir das auch für "Dummies" erklären, bzw. gibt es dazu einen passenden Link?
    Ich weiß derzeit nicht einmal wonach ich suchen soll.

    Wie erstelle ich den Typ "ProduktNeu"?
    Was unterscheidet dann ProduktNeu von Produkt?

    Danke
    Paul

    Dienstag, 14. Mai 2019 12:40
  • Was du oben als "Tabellen" bezeichnest, sind vermutlich die Tabellen aus deiner Datenbank. Du nutzt .NET Core und für den Zugriff auf die Datenbank möglicherweise das Entity Framework, welches dir für den Zugriff auf die Daten/Tabellen der Datenbank Entitäten erstellt. Letztlich sind das Klassen, die ganz spärlich beschrieben mehr oder weniger wie folgt aussehen:

        public class Artikel
        {
            public Guid ID { get; set; }
            public String ArtikelNr { get; set; }
        }
        public class Produkt
        {
            public Guid ID { get; set; }
            public String ProduktNr { get; set; }
            public String Bezeichnung { get; set; }
        }
        public class ProduktArtikel
        {
            public Guid ID { get; set; }
            public Guid ProduktID { get; set; }
            public Guid ArtikelID { get; set; }
            public Int32 Menge { get; set; }
        }

    Hättest du auf Datenbankseite ForeignKeyConstraints (also letztlich Referenzen zwischen den Tabellen) definiert, würden die Entitäten ggf. schon anders aussehen, aber ich nehme das mal so wie im Prinzip von dir oben beschrieben.

    Aus diversen Gründen würde ich diese Entitäten schon nicht 'nach draussen reichen', sondern nur streng innerhalb meiner Anwendung darauf zugreifen. Zudem ist die Art und Weise, wie die Daten in der Datenbank abgelegt/organisiert sind nicht zwangsläufig auch die Art, wie du in deiner Anwendung mit ihnen arbeiten willst. Also baust du dir neue Klassen, welche die Art und weise in der du mit den Daten arbeiten willst besser darstellen, also in etwa so:

        public class ArtikelNeu : Artikel
        { }
    
        public class ProduktNeu : Produkt
        { }
    
        public class ProduktArtikelNeu
        {
            public Guid ID { get; set; }
            public Int32 Menge { get; set; }
    
            public ArtikelNeu Artikeldaten { get; set; }
            public ProduktNeu Produktdaten { get; set; }
    
            public ProduktArtikelNeu(ProduktArtikel pa, Produkt p, Artikel a)
            {
                ID = pa.ID;
                Menge = pa.Menge;
                Artikeldaten = new ArtikelNeu { ID = a.ID, ArtikelNr = a.ArtikelNr };
                Produktdaten = new ProduktNeu { ID = p.ID, ProduktNr = p.ProduktNr, Bezeichnung = p.Bezeichnung };
            }
        }

    Um mir Tipparbeit zu sparen, habe ich ProduktNeu und ArtikelNeu von den jeweiligen Datenbank-Entitäten einfach erben lassen; sie haben dann automatisch die Eigenschaften (Felder) der jeweiligen Eltern-Klasse. Das kann man natürlich auch anders umsetzen.

    ProduktArtikelNeu hat so wie ProduktArtikel auch die Eigenschaften ID und Menge, ProduktID und ArtikelID allerdings nicht. Stattdessen sind die Eigenschaften Produktdaten und Artikeldaten hinzugekommen. Anstatt ProduktArtikel.ProduktID hättest du dann z.B. ProduktArtikelNeu.Produktdaten.ID. Beim Erstellen einer neuen Instanz von ProduktArtikelNeu kannst du dann ProduktArtikel, Produkt und Artikel mit übergeben sodaß die Daten dann gleich auf die neuen Klassen gemappt (übernommen) werden.

    Das Ganze muss dann vermutlich in einer Schleife passieren, um alle Daten aus der Datenbank über die Entitäten der Datenbank in die neuen Klassen zu übernehmen du hättest dann sowas wie ein IEnumerable<ProduktArtikelNeu> welches du im/als Model an die ASP.NET Seite übergeben kannst und dann darauf zugreifen kannst:

    @foreach (var item in Model.ProduktArtikel.OrderBy(s => s.Sort))
    {
    ..
    @Html.DisplayFor(modelItem => item.Produktdaten.ProduktNr)
    @Html.DisplayFor(modelItem => item.Produktdaten.Bezeichnung)
    @Html.DisplayFor(modelItem => item.Menge)
    @Html.DisplayFor(modelItem => item.Artikeldaten.ArtikelNr)
    ..
    }

    Dienstag, 14. Mai 2019 13:15
  • Sorry für die "dürftige" Beschreibung.
    Datenbankseite ForeignKeyConstraints habe ich definiert.
    Bei mir sehen die Klassen folgendermaßen aus:

        public partial class Produkt
        {
            public Produkt()
            {
                ProduktArtikel = new HashSet<ProduktArtikel>();
            }
            public int Id { get; set; }
            public string ProduktNr { get; set; }
            public string Bezeichnung { get; set; }
    		...
    
            public ICollection<ProduktArtikel> ProduktArtikel { get; set; }
        }
    
    
    
        public partial class Artikel
        {
            public Artikel()
            {
                ProduktArtikel = new HashSet<ProduktArtikel>();
            }
            public int Id { get; set; }
            public string ArtikelNr { get; set; }
    		...
    
            public ICollection<ProduktArtikel> ProduktArtikel { get; set; }
        }
    
    
    
        public partial class ProduktArtikel
        {
            public int Id { get; set; }
            public int? ProduktId { get; set; }
            public int? ArtikelId { get; set; }
            public int? Menge { get; set; }
    		...
    
            public Artikel Artikel { get; set; }
            public Produkt Produkt { get; set; }
        }

    Und damit dachte ich, das ich bereits alle Beziehungen habe die ich brauche.
    Oder stehe ich nach wie vor komplett am Schlauch?


    • Bearbeitet Paul_412 Dienstag, 14. Mai 2019 13:54
    Dienstag, 14. Mai 2019 13:43
  • Damit sehen die Klassen eigentlich ganz gut aus, wobei ich ein wenig Bauchschmerzen damit habe, daß die Klasse ProduktArtikel die Klassen Produkt und Artikel beinhaltet und diese wiederum ICollection<ProduktArtikel>, was dann eine 'nette' Verkettung ist, wenngleich es natürlich logisch ist: Ein Artikel kann in mehreren Produkten enthalten sein und umgekehrt.

    Hier bin ich mir beim Verhalten des .NET Core EF nicht sicher, wie die Daten in die Entitäten geladen werden. Setze doch mal einen Breakpoint in deinem Code bevor die Seite aufgerufen wird und schaue dir das Model an (welches dann an die Seite geliefert wird). Vermutlich sind Artikel und Produkt nicht gefüllt (value: null). Dann musst du beim einlesen der Daten ran.

    Dienstag, 14. Mai 2019 14:08
  • Im Prinzip ist das meine Frage.
    Wie fülle ich die Daten?

    Das Problem ist, glaube ich, das ich in der Produkt Seite, welche vom ProduktController aufgerufen wird, Werte anzeigen möchte welche normalerweise im ProduktArtikelController generiert werden.

    Die ProduktArtikel Seiten werden bei Aufruf durch den ProduktArtikelController komplett richtig angezeigt.

    Das der Aufruf

    @foreach (var item in Model.ProduktArtikel.OrderBy(s => s.Sort))
    aus der Produkt Seite so nicht wirklich funktionieren wird, leuchtet mir ein.

    Das war von meiner Seite ein Schuß ins Blaue.
    Ich war schon positiv überrascht dass die Einträge überhaupt angezeigt wurden.


    Die Frage ist halt, wo muss ich schrauben damit die Daten richtig gefüllt werden?

    Dienstag, 14. Mai 2019 14:58
  • Zur Info:
    Habe gerade das Ganze mit Breakpoints angesehen.
    - ProduktArtikel
    - Produkt

    sind gefüllt.
    Auf diese Daten kann ich auch zugreifen

    - Artikel

    ist leer.

    Jemand eine Ahnung wie ich die befüllen kann?
    Dienstag, 14. Mai 2019 15:37
  • wie füllst du denn ProduktArtikel und Produkt derzeit? Hier gibt es eventuell ein LINQ-Statement, welches man dann um Artikel erweitern kann.
    Mittwoch, 15. Mai 2019 05:49
  • Ich rufe die Seite "https://localhost/xxxxx:/Produkt/Details" über den ProduktController auf.

            public async Task<IActionResult> Details(int? id)
            {
                ViewData["Message"] = "Produktverwaltung";
                if (id == null)
                {
                    return NotFound();
                }
    
                var produkt = await _context.Produkt
                    .Include(p => p.Mandant)
                    .Include(p => p.ProduktArtikel)
                    .SingleOrDefaultAsync(m => m.Id == id);
                if (produkt == null)
                {
                    return NotFound();
                }
    
                return View(produkt);
            }

    Da es zwischen ProduktArtikel und Artikel eine Verknüpfung gibt, nehme ich einmal optimistisch an dass sich das selber befüllen soll.

    - Produkt und
    - ProduktArtikel

    sind ja auch richtig befüllt.

    Nur Artikel ist leer.

    Beim debuggen sehe ich das Artikel den Wert "null" hat und vom Typ xx.Models.Artikel ist

    Donnerstag, 16. Mai 2019 07:49