Laden...

Entity Framework CORE und WPF Datagrid

Erstellt von MGernot vor 4 Jahren Letzter Beitrag vor 4 Jahren 3.647 Views
M
MGernot Themenstarter:in
8 Beiträge seit 2009
vor 4 Jahren
Entity Framework CORE und WPF Datagrid

Hallo Leute,
ich bin blutiger Anfänger im Bereich EF Core und WPF und versuche derzeit mir diese Technologien selbst beizubringen.
Also ich habe in SQL Server zwei Tabellen erstellt (ChemischeAnalysen und ChemischeAnalysenDetails) und beide mit einem Fremschlüssel verknüpft. Anschließend habe ich in VS in einem Konsolen-Projekt das Model mit Scaffold-DBContext erstellt. Dann konnte ich einfach mit LINQ die Daten abfragen:


var context = new Models.BetriebsdatenContext();
            var analysen = context.ChemischeAnalysen
                                                .Include(s => s.ChemischeAnalysenDetails)
                                                .ToList();

Das klappt einwandfrei und es kommen auch die korrekten Daten zurückgeliefert. Wobei ich jedoch gnadenlos gescheitert bin ist die Bindung der Daten der untergeordneten Tabelle an ein WPF Datagrid.
Ich kann die Daten der übergeordneten Tabelle ChemischenAnalysen problemlos binden, aber bei der den Chemischen Analysen untergeordneten Liste (ChemischeAnalysenDetails) wird in den Spalten nichts angezeigt und ich bekomme einen Binding Path expression Error für diese Felder.
(Siehe bitte Bild im Anhang.)
Irgendwo fehlt mir hier offensichtlich ein grundsätzliches Verständnis wie diese Bindung gehandhabt wird.Wenn mir hier bitte jemand einen Stubs in die richtige Richtung geben könnte, wäre ich sehr dankbar.Ich habe mir schon einiges zum Thema "WPF Databinding with nested Lists" angesehen, und verstehe einfach nicht warum es bei mir nicht funktioniert.


<Window x:Class="WpfTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfTest"
        mc:Ignorable="d"
        Title="Analysis" Height="450" Width="800" Background="#FF797373">
        
    <Grid>
        <DataGrid x:Name="dataGrid1" HorizontalAlignment="Left" Height="401" Margin="10,10,0,0" VerticalAlignment="Top" Width="668" AutoGenerateColumns="False">

            <DataGrid.Columns>
                <DataGridTextColumn Header="Datum" Binding="{Binding Datum}" Width="100"/>
                <DataGridTextColumn Header="Bezeichnung" Binding="{Binding Bezeichung}" Width="100"/>
                <DataGridTextColumn Header="Anmerkung" Binding="{Binding Anmerkung}" Width="100"/>
                <DataGridTextColumn Header="Gerät" Binding="{Binding Gerät}" Width="100"/>
                <DataGridTextColumn Header="Kategorie" Binding="{Binding Kategorie}" Width="100"/>
                <DataGridTextColumn Header="Element" Binding="{Binding ChemischeAnalysenDetails.Element}" Width="50"/>
                <DataGridTextColumn Header="Wert" Binding="{Binding ChemischeAnalysenDetails.Wert}" Width="50"/>
                <DataGridTextColumn Header="ChemieID" Binding="{Binding ChemischeAnalysenDetails.ChemieId}" Width="*"/>
            </DataGrid.Columns>
        </DataGrid>
        <Button x:Name="btnPopulate" Content="Bind&#xD;&#xA;" HorizontalAlignment="Left" Height="27" Margin="683,10,0,0" VerticalAlignment="Top" Width="100" Click="BtnPopulate_Click"/>

    </Grid>
</Window>


Vielen herzlichen Dank für Eure Hilfe.

Meli

5.657 Beiträge seit 2006
vor 4 Jahren

Wenn ChemischeAnalysenDetails eine Liste ist, dann gibt es dort keine Eigenschaft Element.

Wenn ich es richtig verstehe, hat jeder Datensatz eine Liste von ChemischeAnalysenDetails, und du versuchst, diese in der gleichen Tabelle darzustellen. Wie soll denn das Ergebnis aussehen, das du erwartest?

Weeks of programming can save you hours of planning

W
955 Beiträge seit 2010
vor 4 Jahren

Schau dir mal das MVVM-Pattern an, damit kann man das relativ einfach lösen. Die grundlegende Idee wäre ein MainViewModel zu erstellen welches an die View gebunden wird. Dann benötigt man im ViewModel
* ein IEnumerable<ChemischeAnalyse> "List" für die die Hauptliste (gebunden an DataGrids ItemsSource)
* ein ChemischeAnalyse-Property "Current" das das aktuell im DataGrid gewählte Objekt repräsentiert (gebunden an DataGrids SelectedItem)
* ein IEnumerable<ChemischeAnalyseDetail> "Details" welches an das DetailGrid gebunden wird (ItemsSource)
Dann kann man prima im Setter von Current die Detailliste umhängen: wird ein anderes Objekt gewählt ändert sich Current, dann reicht einfach ein Details = Current.Details;
* ICommand's für Laden, Speichern etc
PS: Klassen sollten im Singular benannt werden um sie von namespaces unterschieden zu können.

M
MGernot Themenstarter:in
8 Beiträge seit 2009
vor 4 Jahren

Hallo Mr. Sparkle,
danke für die Antwort.
Ja,das Ergebnis soll so sein wie in dieser Sicht in SQL Server.


CREATE VIEW [dbo].[ChemischeAnalyse]
AS
SELECT dbo.ChemischeAnalysen.Datum, dbo.ChemischeAnalysen.Bezeichung, dbo.ChemischeAnalysen.Anmerkung, dbo.ChemischeAnalysen.Gerät, dbo.ChemischeAnalysen.Kategorie, dbo.ChemischeAnalysen_Details.Element, dbo.ChemischeAnalysen_Details.Wert, 
             dbo.ChemischeAnalysen_Details.ChemieID
FROM   dbo.ChemischeAnalysen LEFT OUTER JOIN
             dbo.ChemischeAnalysen_Details ON dbo.ChemischeAnalysen.headerId = dbo.ChemischeAnalysen_Details.ChemieID
GO

Die beiden Klassen die EF Core generiert hat schauen so aus:


 public partial class ChemischeAnalysen
    {
        public ChemischeAnalysen()
        {
            ChemischeAnalysenDetails = new HashSet<ChemischeAnalysenDetails>();
        }

        public int HeaderId { get; set; }
        public DateTime? Datum { get; set; }
        public string Bezeichung { get; set; }
        public string Anmerkung { get; set; }
        public string Gerät { get; set; }
        public string Kategorie { get; set; }

        public virtual ICollection<ChemischeAnalysenDetails> ChemischeAnalysenDetails { get; set; }
    }



 public partial class ChemischeAnalysenDetails
    {
        public int Id { get; set; }
        public string Element { get; set; }
        public decimal? Wert { get; set; }
        public int? ChemieId { get; set; }

        public virtual ChemischeAnalysen Chemie { get; set; }
    }
}


Meli

M
MGernot Themenstarter:in
8 Beiträge seit 2009
vor 4 Jahren

Hallo Witte,
danke für die Antwort. Würdest Du diesen Artikel zum Thema empfehlen, den ich im Forum entdeckt habe, oder gibt es zum Thema Viewmodel noch bessere Tutorials/Bücher?
https://www.codingfreaks.de/2017/08/14/wpf-und-mvvm-richtig-einsetzen-teil-5/

Danke

Meli

5.657 Beiträge seit 2006
vor 4 Jahren

Zu MVVM gibt es hier diesen Artikel: [Artikel] MVVM und DataBinding. Das ist aber erst dann interessant, wenn die Daten auch geändert werden sollen. Solange sie nur angezeigt werden sollen, geht es auch so.

Nur ergibt die Ansicht keinen Sinn. Jeder Eintrag in deiner Liste hat ja mehrere Elemente, ChemieIDs etc.

Edit: Achso, ich glaube, jetzt verstehe ich es:


var analysen = context.ChemischeAnalysenDetails
   .Include(s => s.Chemie)
   .ToList();

Weeks of programming can save you hours of planning

M
MGernot Themenstarter:in
8 Beiträge seit 2009
vor 4 Jahren

Hallo,
ja das war ein dummes Beispiel jetzt. Also ChemischeAnalysen representiert einen Header für eine Analyse, ChemischeAnalysenDetails beinhaltet alle unter diesem Header gemessenen Elemente:
Also eigentlich ware das eine Master->Detail ansicht, nur möchte ich in erstem Schritt nur eine flache Liste.

Edit: Toller Artikel, Mr. Sparkle. Den werde ich in jedem Fall durcharbeiten!

Meli

M
MGernot Themenstarter:in
8 Beiträge seit 2009
vor 4 Jahren

Hallo Leute,
danke nochmal für eure Hilfe.Ich habe die Sache jetzt mal so mit JOIN gelöst, damit ich mal ein Erfolgserlebnis habe 😉 Bin immer wieder erstaunt, wie mächtig LINQ ist.
Soll man aber offensichtlich auch so nicht machen, sondern EF Core navigation properties verwenden.


  var analysis = (from a in context.ChemischeAnalysen
                                join p in context.ChemischeAnalysenDetails
                                on a.HeaderId equals p.ChemieId
                                select new
                                {
                                    Datum = a.Datum,
                                    Bezeichung = a.Bezeichung,
                                    Anmerkung = a.Anmerkung,
                                    Gerät = a.Gerät,
                                    Kategorie = a.Kategorie,
                                    Element = p.Element,
                                    Wert = p.Wert

                                }).ToList();

Ich merke jetzt das ich von diesen Technologien und Entwicklermuster viel zu wenig verstehe, um irgendwas sinnvolles auf die Beine zu stellen.

Danke, vorerst und schöne Grüße aus Österreich.

Meli

16.807 Beiträge seit 2008
vor 4 Jahren

Tipp: Wenn Du schon teilweise Deutsch programmierst, was alles andere als eine gute Idee ist, dann lass bitte wenigstens die Umlaute weg.
Es funktioniert zwar theoretisch; aber EF hat das zumindest in der Vergangenheit nicht immer gemocht.

M
MGernot Themenstarter:in
8 Beiträge seit 2009
vor 4 Jahren

Hallo Abt,
ja danke für den Tip,werde ich in Zukunft vermeiden. Das ist so eine dumme Angewohnheit, die ich mir leider bei der VBA Programmierung eingehandelt habe.

Meli

M
184 Beiträge seit 2012
vor 4 Jahren

Ich wundere mich gerade, dass noch gar nicht der Einwand kam, dass Datenbank Entitäten nicht an die UI gebunden werden, sondern dafür ViewModels benutzt werden sollten. 🤔

16.807 Beiträge seit 2008
vor 4 Jahren

Wurde implizit bereits durch den Hinweis auf mvvm gemacht.
Und wenn man nur liest ist das eben nur halb so schlimm.