Laden...

[erledigt] List<T> und Änderungen darin in Datenbank speichern

Erstellt von mygil vor 12 Jahren Letzter Beitrag vor 12 Jahren 5.218 Views
M
mygil Themenstarter:in
124 Beiträge seit 2009
vor 12 Jahren
[erledigt] List<T> und Änderungen darin in Datenbank speichern

Hallo an alle!

Ich möchte mich mit der Objektorientierten Programmierung vertraut machen und habe dafür auch schon einiges an Material besorgt. Leider habe ich bis jetzt eher mehr Theorie als Praxis Beispiel gefunden und würde gerne mal von euch wissen, ob ich mit meiner kleinen Bestell-Appliaktion in etwa auf dem richtign weg bin:

Ich verwende:
Visual Studio 2010
MS SQL 2008
Tabellen: [Bestellung] + [BestellungDetails]
Klassen: Bestellung, BestellungDetails, BestellungService, BestellungDetailsService

Klasse Bestellung sieht so aus:

    public class Bestellung
    {
        public int BestellNr { get; set; }
        public DateTime BestellDatum { get; set; }
        public List<BestellungDetail> BestellungDetails { get; set; }
    }

Klasse BestellungService sieht so aus:

    public static class BestellungService
    {
        public static List<Bestellung> GetBestellungen()
        {
            List<Bestellung> bestellungen = new List<Bestellung>();

            DataTable myTable = myUms.mySql.GetDataTableFromQuery(@"SELECT * FROM dbo.Bestellung");
            foreach (DataRow row in myTable.Rows)
            {
                Bestellung bestellung = new Bestellung();

                bestellung.BestellNr = (int)row["BestellNr"];
                bestellung.BestellDatum = (DateTime)row["Datum"];
                bestellung.BestellungDetails = BestellungDetailService.GetBestellungDetails((int)row["BestellNr"]);

                bestellungen.Add(materialBestellung);

            }
            return bestellungen;
        }
    }

Der Zweck der Klasse BestellungService ist, dass sie mir einfach eine Liste aller vorhanden Bestellungen inkl. der Bestelldetails/positionen liefert.

Meine Frage lautet: Bin ich so auf dem richtigen Weg?

Falls ja, wie speichert man jetzt Änderungen auf dem SQL Server?
Also wenn ich jetzt z.b. Bestelldetails ändere (z.b. einen Position lösche und eine hinzufüge) dann ändere ich das ganze ja nur in einer Liste (List<BestellDetails>) in einer Bestellung!

Danke für eure Hilfe im Voraus!
lg myGil

C
80 Beiträge seit 2010
vor 12 Jahren

Hallo mygil,

Grundsätzlich ist die Vorgehensweise richtig. Wenn du Änderungen innerhalb einer Liste nicht manuell verfolgen willst, kannst du anstatt List<T> eine ObservableCollection<T> nehmen.

Bei dieser kannst du dich auf das Event CollectionChanged hängen, dass dir in den EventArgs die Action (gelöscht, geändert, neu) sowie die betroffenen Elemente liefert. Dann musst du nur die Action auswerten und entsprechend bei Neu auf die Datenbank ein insert, bei geändert ein update mit entsprechendem where-filter und bei gelöscht ein delete mit entsprechendem where-filter machen.

Eleganter geht das ganze auch mit O/R-Mappern wie dem Entity Framework.

6.911 Beiträge seit 2009
vor 12 Jahren

Hallo mygil,

wie CSharperUser schon angemerkt hat geht das mittels O/R-Mappern eleganter. Siehe zB Absolute Beginners Guide to Entity Framework.

Auf diesem Wege ist dann auch Using Repository and Unit of Work patterns with Entity Framework 4.0 interessant.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

M
mygil Themenstarter:in
124 Beiträge seit 2009
vor 12 Jahren

Hallo!

Das klingt schon mal ganz gut 😃

Aber dadurch werden alle Änderungen immer sofort durchgeführt!
Ich möchte aber, dass die Änderungen erst dann tatsächlich gespeichert werden, wenn der Benutzer auf [Speichern] drückt! (Screenshot)

Wie könnte man das mit OO realisieren?

Bevor ich mit der OO begonnen habe, habe ich sowas immer mit DataTable und SQLDataAdapter gemacht! Der Programmieraufwand dafür war nahezu null:

        public int              Update(DataTable myTable, string cmdText)
        {
            da = new SqlDataAdapter(cmdText, con);
            SqlDataAdapter daFürCommBuilder = new SqlDataAdapter(cmdText, con);
            SqlCommandBuilder cb = new SqlCommandBuilder(daFürCommBuilder);

            da.InsertCommand = cb.GetInsertCommand();

            DataColumn dc = null;
            foreach (DataColumn c in myTable.Columns)
            {
                if (c.AutoIncrement == true)
                {
                    dc = c;
                    break;
                }
            }
            if (dc != null)
                da.InsertCommand.CommandText = da.InsertCommand.CommandText + " SELECT SCOPE_IDENTITY() As " + dc.ColumnName;

            da.InsertCommand.UpdatedRowSource = UpdateRowSource.Both;
            da.UpdateCommand = cb.GetUpdateCommand();
            da.UpdateCommand.UpdatedRowSource = UpdateRowSource.Both;
            da.DeleteCommand = cb.GetDeleteCommand();
            da.UpdateCommand.UpdatedRowSource = UpdateRowSource.Both;

            return DataAdapterUpdate(myTable);
        }

Hoffe das es für diese Objekte eine ähnlich elegante Lösung gibt!

lg myGil

6.911 Beiträge seit 2009
vor 12 Jahren

Hallo mygil,

Ich möchte aber, dass die Änderungen erst dann tatsächlich gespeichert werden, wenn der Benutzer auf [Speichern] drückt! (Screenshot)

Dann schau dir die Links (oben) an. Nur wenn SaveChanges aufgerufen wird werden die Daten gespeichert. Das ist auch das Grundprinzip einer Unit of Work.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

M
mygil Themenstarter:in
124 Beiträge seit 2009
vor 12 Jahren

Hallo!

Das "Absolute Beginners Guide to Entity Framework" bin ich eben durch!
Vor ein paar Tagen habe ich mich mit Repository und Interface in 3 Schichten Artiketur beschäftgt und damit das ganze für mich an Transparenz gewinnt lerne ich lese ich nebenher noch ein Buch über UML...

Aber ich habe immer mehr das Gefühl, dass ich "vom 100ste ins 1000ste" gerate.

Mein Hauptprojekt an dem ich Arbeite hat derzeit 131 Forms und 69 Tabellen.
Darin möchte ich jetzt ein Bestellmodul erstellen, dass (vorallem aus lerngründen) OOer werden soll.

Ob das ganze tatsächlich eine Gute Idee ist, versuche ich erst noch herauszufinden! Dank gfoidl's Hilfe (in einem älteren Beitrag) hatte ich auch schon wirklich erstaunliche Erfolge und Spass am Klassen erstellen bzw. konnte diese sehr brauchbar einsetzten! (Am besten gefällt mir der Teil, wie einfach und übersichlich sich so z.b. Bestellsumme errechnen lässt ^^)

    public class MaterialBestellung
    {
        ...
        public List<MaterialBestellungDetail> MaterialBestellungDetail { get; set; }
        public decimal  BestellSumme 
        {
            get
            {
                decimal dBestellSumme = 0;
                foreach (MaterialBestellungDetail d in this.MaterialBestellungDetail)
                    dBestellSumme += d.GesamtPreis;
                return dBestellSumme;
            }
        }
    }

Aber ich vermute, wenn ich jetzt anfange Entity's in meinem Projekt zu erstellen (vielleicht auch noch Interfaces, 3-Schichten-Architekur, Patterns ...), dann komme ich mit meinem Wissenstand mehr ins schleudern als wie ich einen nutzen daraus ziehen könnte...

Was meint ihr? Es es Sinnvoll ohne Entity's etc. versuchen auszukommen und ja wie löse ich mein Problem? (Erst speichern nach dem betätigen des Speicherbuttons)

Danke für eure Hilfe (wiedermal!)
myGil

6.911 Beiträge seit 2009
vor 12 Jahren

Hallo mygil,

wenn ich jetzt anfange Entity's in meinem Projekt zu erstellen (vielleicht auch noch Interfaces, 3-Schichten-Architekur, Patterns ...), dann komme ich mit meinem Wissenstand mehr ins schleudern als wie ich einen nutzen daraus ziehen könnte...

Ich verwende Entities* - aber das heißt nicht dass es ohne auch geht, es ging ja früher auch 😉 bzw. gibt es noch viele andere O/R-Mapper.
* mit ADO.NET Entity Framework 4.1 gehts praktischer und du hast auch gut POCO Unterstützung.

Wenn du dich damit beschäftigen willst dann probier das alles an einem kleinem Beispiel-Programm aus und wenn du dann fit bist übertrage das Gelernte auf das reale Projekt.

Erst speichern nach dem betätigen des Speicherbuttons

Ist kein Problem - ruf einfach SaveChanges im Event-Handler des SpeicherButton-Click-Ereignisses auf.
Wie das bei DataTable geht weiß ich nicht.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

A
350 Beiträge seit 2010
vor 12 Jahren

Hi,

was spricht eigtl. gegen ein Repository, welches dir die Arbeit abnimmt ?
Da kannst du dann mit "klassischem" SQL arbeiten und die Daten auf objekte mappen.

Wenn du mit dem EF arbeitest, hast du den riesen Vorteil, keine eignenen Objekte mehr schreiben zu müssen.
Du kannst auch Daten an Objekte Binden und und und.

So wie gfoidl es geschrieben hat : EF und beim Eventhandler des Buttons ein Context.SaveChanges() aufrufen wird dein Problem lösen 😃

M
mygil Themenstarter:in
124 Beiträge seit 2009
vor 12 Jahren

Wie würde den so ein Repository aussehen?

Verwendete ich nicht schon ein Repository:

    public static class BestellungService
     {
         public static List<Bestellung> GetBestellungen()
         {
             List<Bestellung> bestellungen = new List<Bestellung>();
 
            DataTable myTable = myUms.mySql.GetDataTableFromQuery(@"SELECT * FROM dbo.Bestellung");
             foreach (DataRow row in myTable.Rows)
             {
                 Bestellung bestellung = new Bestellung();
 
                bestellung.BestellNr = (int)row["BestellNr"];
                 bestellung.BestellDatum = (DateTime)row["Datum"];
                 bestellung.BestellungDetails = BestellungDetailService.GetBestellungDetails((int)row["BestellNr"]);
 
                bestellungen.Add(materialBestellung);
 
            }
             return bestellungen;
         }
     }

Und habs das halt nur Service genannt?

Nicht einfach der Einstieg! Für mich heißt das mit EF zu arbeiten bei quasi null anzufangen 😦

lg myGil

6.911 Beiträge seit 2009
vor 12 Jahren

Hallo mygil,

Wie würde den so ein Repository aussehen?

Weiter oben in einem Beitrag von mir gibts dazu einen Link. Schau dir den mal an.
Entscheide dich auch jetzt ob du in Zukunft mit DataTabel oder O/R-Mapper arbeiten willst. Dann entscheide dich für einen O/R-Mapper wie zB das ADO.net EF (4.1).
Alles andere ist nicht zielführen und trägt eher zur Verwirrung bei (wie du ja selbst merkst 😉).

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

M
mygil Themenstarter:in
124 Beiträge seit 2009
vor 12 Jahren

Hallo nochmals!

Zum probieren habe ich jetzt eine Videothek Software gebaut.
Entity Kunden
Entity Filme
Entity Verleih (Welcher Kunde was ausgeliehen hat)
SQL View

Ich nenne sie schlicht: "Online Video Management System 6.0" 😃

Aber irgendwie vermisse ich jetzt meinen Klassen - ist das normal, dass ich jetzt keine Class.cs Dateien im Projekt habe und stattdessen nur dieses Model.edmx (+Designer.cs)

Oder habe ich was wichtiges ausgelassen??

Danke nochmals für all die Hilfe!!!
@gfoidl wir sind fast Nachbarn! (Bin Wörgler ^^)

A
350 Beiträge seit 2010
vor 12 Jahren

Hi,

ja , deine Klassen die du suchst sind in den Designer.cs Dateien versteckt.
Also alles gut 😉

6.911 Beiträge seit 2009
vor 12 Jahren

Hallo mygil, Hallo fast Nachbar,

oder du verwendest das EF 4.1 (siehe Link oben) und damit kannst du POCOs erstellen - also normale Klassen die keine Abhängigkeit zu einem O/R-Mapper haben. Siehe hierzu auch EF 4.1 Model & Database First Walkthrough

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

M
mygil Themenstarter:in
124 Beiträge seit 2009
vor 12 Jahren

Hallo!

Jetzt habe ich mich mit diesem EF 4.1 Model & Database inkl. Moco's mal genauer angeschaut!

Damit hatte ich geniale Erfolge!

Aber als ich so gut wie fertig war und mir nochmals diese "Model1.edmx" etc. anschauen wollte wuden alle meine Änderungen in den Klassen die ich machte überschrieben:

//------------------------------------------------------------------------------
// <auto-generated>
//    Dieser Code wurde aus einer Vorlage generiert.
//
//    Manuelle Änderungen an dieser Datei führen möglicherweise zu unerwartetem Verhalten Ihrer Anwendung.
//    Manuelle Änderungen an dieser Datei werden überschrieben, wenn der Code neu generiert wird.
// </auto-generated>
//------------------------------------------------------------------------------

Menie Frage: Was bringen mir jetzt diese Klassen - wenn ich sie nicht bearbeiten darf?

Und wiedermal: Danke danke danke fürs Helfen!!! 😃

lg myGil

6.911 Beiträge seit 2009
vor 12 Jahren

Hallo mygil,

inkl. Moco's

Die heißen POCO von Plain Old CLR Object - also normale Objekte ohne dass sie Besonderheiten eines O/R-Mappers, etc besitzen.

Ja die von Designer generierten Klassen sollte man nicht angreifen. Damit darin aber Änderungen gemacht werden können sind diese mit partial versehen. Schau also mal nach "partiellen Klassen" um Infos zu finden wie damit gearbeitet werden kann.

Ich würde aber dennoch nix an den Klassen rumwerken - auch nciht mit partial - und stattdessen lieber ein Repository (siehe weiter oben) implementieren und dort die nötige Funktionalität umsetzen. Probier auch ein Wenig um mit welcher "Strategie" du deine Repositories umsetzen willst. Es gibt verschiedene Wege und jeder handhabt das ein wenig anders. Such dir selbst einen Weg und überlege immer ob es noch testbar ist.

Was bringen mir jetzt diese Klassen - wenn ich sie nicht bearbeiten darf?

Darin befindet sich der generierte Code für den Modell-Klassen des Datenzugriffs. Sie sind also ein wesentlicher Bestandteil damit der O/R-Mapper überhaupt arbeiten kann. Da diese jedoch bei jedem Speichern der *.edmx oder *.tt überschrieben werden sollte eben darin nix manipuliert werden.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

M
mygil Themenstarter:in
124 Beiträge seit 2009
vor 12 Jahren

Hallo gfoidl!

Heute habe ich den ganzen Tag damit verbraucht, mich mit diesen "ADO.NET Entity Framework 4.1 + POCO's zu befreunden!

Die sind mir Heute richtig ans Herz gewachen, das einzige was mir noch fehlt ist:

    public class MaterialBestellungDetail
    {
        ...
        public decimal GesamtPreis
        {
            get { return this.EinzelPreis * Menge; }
        }
    }

Bzw. siehe Screenshot!
Das muss doch irgendwie gehen - wie löst du sowas?

Danke!
lg myGil

6.911 Beiträge seit 2009
vor 12 Jahren

Hallo mygil,

das geht normal auch über die partiellen Klassen. ZB


partial WWS_MaterialBestellungDetail
{
    public decimal GesamtPreis { get { return ... ; } }
}

wobei de WWS_MaterialBestellungDetail eine generierte Klasse ist.

Allerdings bin ich mir jetzt nicht sicher ob da EF nicht meckern beginnt, da diese Eigenschaft nicht auf die Datenbank gemappt wird. Probiers einfach mal aus (ich müsste es ja auch probieren 😉).

Edit: Computed properties in ADO.NET Entity Framework es scheint nicht zu funktionieren. Aber schau dir die Links darin an - vllt. findest du ein "Workaround".

Edit2: Der einfachste Ausweg daraus ist diese berechnete Eigenschaft nicht im Model zu haben sondern entsprechend im UI-Model - sprich PresentationModel (WinForms) od. ViewModel (WPF). Dort gehört es ja eigentlich auch hin.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

M
mygil Themenstarter:in
124 Beiträge seit 2009
vor 12 Jahren

Hallo!

Ganz Allgemeine Frage:

In der OO heißt es doch immer, dass man aus allem einem Objekt machen soll...

Beispiel PKW:
Kann: fahren() anhalten()
Eigenschaften: Farbe
Ist eine spezialisierung von Fahrzeug (abgeleitete Klasse)
...

Diese Klassen aus "EF 4.1 Model & Database inkl. Moco's" haben damit überhaupt nichts mehr zu tun ... irgendwie scheints mir, als wäre ich jetzt vom Weg abgekommen?! X(

Schlage ich mit den "Repositories" wieder den richtig Weg ein?

lg myGil

6.911 Beiträge seit 2009
vor 12 Jahren

Hallo mygil,

sehr gute Frage die auch die Meinungen spaltet wie du in 3-Schichten-Design?? [harte vs. weiche (Daten-)Objekte / OOP vs. ActiveRecods] nachlesen kannst. Deine Überlegung ist vollkommen richtig. Zumindest von der Theorie her.

In der Praxis hat es aber in diesem speziellen Fall Vorteile wenn es nur ein Objekt mit Daten und ohne Verhalten ist (DTO...Data Transfer Oject). Für nähere Infos verweise ich auch Design Patterns for Data Persistence und bitte das hier nicht weiter zu diskutieren wegen [Hinweis] Wie poste ich richtig? Punkt 1.2. So interessant das auch sein könnte - aber dafür gibts eh obigen Link 😉

Schlage ich mit den "Repositories" wieder den richtig Weg ein?

Meiner Meinung nach: ja

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

M
mygil Themenstarter:in
124 Beiträge seit 2009
vor 12 Jahren

Dann werde ich mir demnächst die Repositories genauer ansehen!!!

Danke für alles!!!

M
mygil Themenstarter:in
124 Beiträge seit 2009
vor 12 Jahren

Hi!

Falls es jemanden interessiert:

Hallo mygil,

das geht normal auch über die partiellen Klassen. ZB
partial WWS_MaterialBestellungDetail
{
public decimal GesamtPreis { get { return ... ; } }
}
wobei de WWS_MaterialBestellungDetail eine generierte Klasse ist.
Allerdings bin ich mir jetzt nicht sicher ob da EF nicht meckern beginnt, da diese Eigenschaft nicht auf die Datenbank gemappt wird.

Das mit den partiellen Klassen scheint perfekt mit Entify Framework 4.1 zu funktionieren! Einfach dafür eine neue Klasse Namens (z.b.) "DataModelExtensions.cs" erstellen und folgenden Code verwenden:

--> Bestellung ist das im Model1.edmx erstellt Objekt und wird sozusagen erweitert.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace WindowsFormsApplication1
{
    public partial class Bestellung
    {
        public decimal BestellSumme
        {
            get
            {
                var q = from bd in this.BestellungDetail select bd.Einzelpreis * bd.Menge;
                return q.Sum();
            }
        }
        public decimal BestellMenge
        {
            get
            {
                var q = from bd in this.BestellungDetail select bd.Menge;
                return q.Sum();
            }
        }
    }
}