Laden...

SQL Statement zu LINQ

Erstellt von Alesia vor einem Jahr Letzter Beitrag vor einem Jahr 952 Views
A
Alesia Themenstarter:in
15 Beiträge seit 2022
vor einem Jahr
SQL Statement zu LINQ

Verwendetes Datenbanksystem: <SQL>

Hallo zusammen,

ich versuche irgendwie verzweifelt ein SQL Statement in LINQ abzubilden.
Zur Verfügung habe ich 3 einzelne Datenbanktabellen, dessen Keys jeweils Adressen (adrnr), Anschriften (adrnr, ansnr) und Ansprechpartner (adrnr, ansnr, aspnr) sind.
Was ich aktuell habe, bringt nicht das Ergebnis was ich versuche abzubilden.

Ich suche im konkreten von hinten nach vorne

Den Standart Ansprechpartner (der jeweiligen Anschrift), der Anschrift(en), der Adresse. 1:n:n
Eine Adresse hat IMMER mind. 1 Anschrift und kann mehrere haben, aber nicht jede Anschrift hat zwingend einen Ansprechpartner (wovon eine Anschrift aber mehrere haben kann), wenn dann muss der Ansprechpartner mit dem StdKz == true abgerufen werden

Anhand folgendes Beispiels hatte ich mich schon versucht, jedoch kriege ich damit nur Adressen zurück die einen Ansprechpartner haben.
Wenn kein Ansprechpartner vorhanden ist, kriege ich auch die jeweilige Adresse nicht.

https://www.codeproject.com/Questions/5301181/How-to-use-linq-to-join-3-tables-in-Csharp

Hat jemand einen entscheidenen Tipp für mich?

Mein aktueller Code:


            var adressen = await _context.Adressen
                        .Include(b => b.Bild)
                        .Include(s => s.Status)
                        .ToListAsync();
            var anschriften = await _context.Anschriften
                        .ToListAsync();
            var ansprechpartner = await _context.Ansprechpartner
                        .Include(b => b.Bild)
                        .ToListAsync();

            var adressenListe =
            (
                from adr in adressen
// Inner Join da immer eine Anschrift existiert, bezogen auf die Adressnummer
                join ans in anschriften on adr.AdrNr equals ans.AdrNr
// Müsste ein LEFT Join sein, der als where ans.AdrNr == asp.AdrNr && ans.AnsNr == asp.AnsNr && asp.StdKz == true beinhaltet
                join asp in ansprechpartner on ans.AnsNr equals asp.AnsNr


                select new AdressenViewModel()
                {
                    ID = ans.ID,
                    ERPID = ans.ID,
                    AdrNr = ans.AdrNr,
                    AnsNr = ans.AnsNr,
                    SuchBeg = adr.SuchBeg,
                    Branche = adr.Branche,
                    VtrNr = adr.VtrNr,
                    Na1 = ans.Na1,
                    Na2 = ans.Na2,
                    Na3 = ans.Na3,
                    Str = ans.Str,
                    PLZ = ans.PLZ,
                    Ort = ans.Ort,
                    LandBez = ans.LandBez,
                    Tel = ans.Tel,
                    EMail1 = ans.EMail1,
                    WSeite = ans.WSeite,
                    StdReKz = ans.StdReKz,
                    StdLiKz = ans.StdLiKz,
                    StdAnsprechpartner = asp.VNa + " " + asp.NNa,
                    Info = ans.Info,
                    Bild = adr.Bild,
                    GspKz = ans.GspKz,
                    ErstBzr = ans.ErstBzr,
                    AendBzr = ans.AendBzr,
                    ErstDat = ans.ErstDat,
                    LtzAend = ans.LtzAend
                });


A
Alesia Themenstarter:in
15 Beiträge seit 2022
vor einem Jahr

Danke für die Antworten.
Die Microsoft Hilfe beschreibt keine Where Clausel innerhalb des Joins.
Ebenso arbeite ich in einer Apple Umgebung und kann den Converter nicht nutzen.

Jedoch habe ich wie es so oft der Fall ist, nochmal drüber nachgedacht.
Der Fehler lag nie an meinem LINQ Tests, sondern an dem Model das ich nicht gegen NULL geprüft habe.

Der vollständige LINQ zu meiner Antwort ist:


                from adr in adressen
                join ans in anschriften on adr.AdrNr equals ans.AdrNr
                from asp in ansprechpartner.Where(a => ans.AnsNr == a.AnsNr && a.StdKz == true).DefaultIfEmpty()

Das ausschlaggebende ist jedoch gewesen:


                    StdAnsprechpartner = asp == null ? "" : asp.VNa + " " + asp.NNa,


Somit wäre das Problem gelöst 😉

16.806 Beiträge seit 2008
vor einem Jahr

Du hast da extremen Spaghetti-Code, ein Horror das zu pflegen. Ist Dir bewusst, oder? 🙂
Schau Dir [Artikel] Drei-Schichten-Architektur

  • Man hat keinen DB Code in Views
  • Man hat keine ViewModels im DB Code

Verwende für Rückgabe von DB-Selects einfach Projektionen. Macht man mit allen DB-Systemen und in allen Programmiersprachen/Runtimes so.

A
Alesia Themenstarter:in
15 Beiträge seit 2022
vor einem Jahr

Sind auch meine ersten Versuche in dem Umfeld, man liest eben sehr viel unterschiedliches.
Use-Cases, Pluginmethode, Drei-Schichten-Architektur.

Ich kann das noch nicht ganz so verstehen mit deinem Ansatz, ist das so in etwa der Weg?

Müsste meine Datenzugriffsschicht den anonymen select als return liefern, die Logikschicht müsste ein Viewmodel draus machen und die Präsentationsschicht ruft das fertige Model ab, zeigt an und gibt Änderungen wieder an die Logikschicht, die wiederum aus dem ViewModel ein DB Model macht, das dann in der Datenzugriffsschicht verarbeitet wird?

Ich hab ein relativ altes Video gefunden, dass erstmal logisch klingt.
Ist das so zu empfehlen?
3 Schichten Architektur

16.806 Beiträge seit 2008
vor einem Jahr

Müsste meine Datenzugriffsschicht den anonymen select als return liefern,

Es ist nicht möglich anonyme Typen zurück zu geben.
Die Datenschicht sollte eigene Modelle haben, die zurück gegeben werden. Man spricht hier von Projektionen.
IBM IT Basics: Selection and projection
Ist zwar von IBM, aber trotzdem eine korrekte Definition.

Hintergrund: man braucht nicht immer eine Entität, sondern man benötigt in 99% der Fällen ja ein gewisses Gesamt-Select einer Datenbank: die Projektion.
Neben der Entität liefert also die Datenschicht noch viele weitere Modelle, die man dann in der UI-Schicht konsumieren kann; zB in ViewModels übersetzen kann.

die Logikschicht müsste ein Viewmodel draus machen und die Präsentationsschicht ruft das fertige Model ab, zeigt an und gibt Änderungen wieder an die Logikschicht, die wiederum aus dem ViewModel ein DB Model macht, das dann in der Datenzugriffsschicht verarbeitet wird?

Siehe [Artikel] Drei-Schichten-Architektur
Nimms mir nich übel, aber auch wenn Dein verlinktes Video 14 Jahre alt is, werd ich das Video nun nicht vollständig anschauen können und reviewen, um zu sagen, obs was ist oder nicht. Da fehlt mir dann doch die Zeit für.
Die 3-Schicht-Architektur ist aber ein bewährtes Standard-Vorgehen, das es schon Jahrzehnte gibt und immer noch Gültigkeit hat. Es gibt aber natürlich auch neuere/modernere Ansätze, je nach Nutzen. Aber man sollte die 3-Schicht-Architektur schon kennen, weil sie die Basis vieler moderner Vorgehen ist.

A
Alesia Themenstarter:in
15 Beiträge seit 2022
vor einem Jahr

Bin dir ja dankbar das du antwortest, dann musst du nicht noch ein Video anschauen 😉
Aber die Grundstruktur entspricht dem was du verlinkt hast.

Grundsätzlich habe ich die Struktur so auch verstanden, wo es gedanklich aber noch hängt ist bei der Übergabe der Daten in den Service.

Das Datenbank ist unterteilt in 4 Tabellen, die für den Abruf einer "Anschrift" in meinem Fall benötigt werden.
Ich würde jetzt als nächstes 3 weitere Datenmodell Klassen bilden, die meine anderen benötigten Tabellen abruft.

Im Service ruft ich also meine Daten von jedem Datenmodell ab.
Mache ich dann im Service schon das ViewModel oder Injecte ich den Service in meine blazor Komponente und mach da erst ein ViewModel draus?

16.806 Beiträge seit 2008
vor einem Jahr

Der Service sollte das ViewModel nicht kennen. Das ViewModel ist Teil der UI. Das kennt also nur die UI.
Siehe Bild in [Artikel] Drei-Schichten-Architektur

  • Oben UI
  • Mitte Logik
  • Unten Daten

Modelle dürften von Unten nach Oben bekannt sein, aber nicht von Oben nach Unten.
UI darf die Datenmodelle kennen, aber Daten kennt keine UI Modelle. Ein Service aus der unteren Schicht kann also niemals ein ViewModel nutzen.

A
Alesia Themenstarter:in
15 Beiträge seit 2022
vor einem Jahr

Alles klar verstanden, soweit dachte ich es mir schon.
Hab gerade meine Test App parallel dazu mal in das neue System gebracht.
Aktuell hänge ich noch daran, dass in der programm.cs ein Fehler kommt, den ich gerade er-google.

"Some services are not able to be constructed"


using test.Service;

builder.Services.AddScoped<IAdresseService, AdresseService>();

A
Alesia Themenstarter:in
15 Beiträge seit 2022
vor einem Jahr

Kann das sein, dass man seine Beiträge nicht bearbeiten kann?

Ich werde einfach nicht fündig zu dem Problem, sorry für den vielen Code, aber ich verstehs an dem Punkt nicht weshalb das passiert.

Drei Klassenbibliotheken und ein Server Projekt
Data
Service
Model
Blazor Server = App

Referenz in Data = Service / Model
Referenz in Service = Data / Model
Referenz in App = Service

In Data ist der DBContext, ganz normal wie er sein sollte.


    public class DataContext : DbContext
    {
        public DataContext(DbContextOptions<DataContext> options) : base(options)
        {
        }
        public DbSet<Adresse> Adresse { get; set; }
    }
}

In Data wird mein Repository abgerufen


        private readonly DataContext _context;

        public AdresseRepository(DataContext context)
        {
            _context = context;
        }
        public async Task<List<Adresse>> GetAdressen()
        {

            var adressenTable = await _context.Adresse
                            .ToListAsync();
            return adressenTable;
        }

Im Service ist eine Klasse die das Repository in Data abruft


	public class AdresseService : IAdresseService
    {
        private IAdresseRepository _adresseRepository;

        public AdresseService(IAdresseRepository adresseRepository)
        {
            _adresseRepository = adresseRepository;
        }

        public async Task<List<Adresse>> GetAdressen()
        {
            List<Adresse> adressenTable = new List<Adresse>();
            adressenTable = await _adresseRepository.GetAdressen();
            return adressenTable;
        }
    }

In der Index.razor per @inject IAdresseService adresseService, rufe ich var adressenTable = await adresseService.GetAdressen(); auf

In der Program.cs ist der Context und der Service registriert


builder.Services.AddDbContext<DataContext>
                (options => options.UseSqlServer("String"));

builder.Services.AddScoped<IAdresseService, AdresseService>();

Beim starten der App bekomme ich "Some services are not able to be constructed (NET.Service.IAdresseService) bezogen auf NET.Data.IAdressRepository
> Beim ganzen lesen auf google würde ich sagen, dass irgendwo die Referenz zur Klassenbibliothek fehlt.
> Hab was von DBContext Factory gelesen und so, aber irgendwie komm ich nicht weiter.

Gibt es bereits einen Artikel der mein Problem behandelt und ggf. lösen kann, oder habt ihr eine Idee dazu, wo ich was vergessen habe?

4.931 Beiträge seit 2008
vor einem Jahr

Hast du denn auch IAdresseRepository mit der Klasse AdresseRepository registriert?

16.806 Beiträge seit 2008
vor einem Jahr

Ja, gibt ein Artikel - sollte man sich meist vor dem Coden durchlesen 😉Dependency injection - .NET
Dependency Injection muss alle Services kennen, sonst kann es nicht funktionieren, wie TH69 schon eine potentielle Lösung dazu gesagt hat.

A
Alesia Themenstarter:in
15 Beiträge seit 2022
vor einem Jahr

Vielen Dank,
das war auch die Lösung.
Ich hatte den Repository Service nicht registriert, auch nicht wirklich realisiert das die "App" den Service kennen muss, da die App keine Projektreferenz zu der Datenschicht hat.
Dachte das schlingt sich von Schicht zu Schicht, sodass nur die darüberliegende vom drunterliegenden Service wissen muss.
Die richtige Reihenfolge der Registrierung ist auch wichtig, das war mein nächster Fehler.

16.806 Beiträge seit 2008
vor einem Jahr

Die richtige Reihenfolge der Registrierung ist auch wichtig, das war mein nächster Fehler.

Dann stimmt was bei Dir nicht.
Die Reihenfolge der Registrierungen von Services - bei ASP.NET Core inkl Blazor findet das in ConfigureServices() statt - muss immer irrelevant sein, weil diese nicht garantiert ist (zB durch Fremdabhängigkeiten).
Die Reihenfolge der Middleware (also bei ASP.NET Core in Configure()) ist jedoch genau zu beachten.
ASP.NET Core Middleware Order

A
Alesia Themenstarter:in
15 Beiträge seit 2022
vor einem Jahr

Ja da hattest du recht.
Ich bin das nochmal durchgegangen und hab das alles behoben.
Läuft nun nach dem 3-Schichten-Prinzip wie du vorgeschlagen hast.
Bringt schon Vorteile mit sich, das stimmt.