Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Community
  • |
  • Diskussionsforum
Logik zum anlegen/bearbeiten von Datensätzen in DB-Anwendung
sugar76
myCSharp.de - Member



Dabei seit:
Beiträge: 66
Herkunft: Berlin

Themenstarter:

Logik zum anlegen/bearbeiten von Datensätzen in DB-Anwendung

beantworten | zitieren | melden

Hallo,

ich möchte gerne mal zur Diskussion stellen, wie die Logik zum Anlegen/Bearbeiten von Datensätzen in einer klassischen Datenbank-Anwendung idealerweise implementiert wird.

Technologien: WPF und Entity Framework.

Hierzu ein Mini-Beispiel, um Email-Adressen in der DB anzulegen/bearbeiten. Jede E-Mail-Adresse besitzt zwei Felder:
- Die eigentliche Adresse
- Einen optionalen Typ, z.B. Privat, Arbeit, ...

Ich habe es so umgesetzt, dass mit Detached-Objekten arbeite, die
- an ein ViewModel zur Bearbeitung übergeben werden
- vom Benutzer editiert werden
- dann zum Speichern an einen Service.

Wie würdet Ihr das machen?

Gruß

View:

<UserControl x:Class="MyApp.View.EmailEditView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             mc:Ignorable="d" 
             Height="Auto" Width="400" x:Name="View">
    <DockPanel>         
        <Grid DockPanel.Dock="Top">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <!-- Email-Adresse -->
            <Label Grid.Row="0" Grid.Column="0" Content="Email-Adresse"/>
            <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Adresse, UpdateSourceTrigger=LostFocus, ValidatesOnNotifyDataErrors=True}" />
            
            <!-- Email-Typ -->
            <Label Grid.Row="1" Grid.Column="0" Content="Typ"/>
            <ComboBox
                Grid.Row="1" Grid.Column="1" 
                ItemsSource="{Binding EmailTypList}" 
                SelectedItem="{Binding EmailTyp, Mode=TwoWay}" 
                DisplayMemberPath="Name" />
        </Grid>

        <!-- Speichern/Abbrechen -->
        <StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" HorizontalAlignment="Right">
            <Button Content="Speichern" Command="{Binding Save}" CommandParameter="{Binding ElementName=View}" />
            <Button x:Name="BtnCancel" Content="Abbrechen" Click="BtnCancel_Click" />
        </StackPanel>
    </DockPanel>
</UserControl>

ViewModel:


namespace MyApp.ViewModel
{
    public class EmailEditViewModel : ValidatingViewModelBase
    {
        private MyApp.Entities.Email email;
	
        private string adresse;
        public string Adresse
        {
            get { return adresse; }
            set { adresse = value; OnPropertyChanged(); }
        }

        private MyApp.Entities.EmailTyp emailTyp;
        public MyApp.Entities.EmailTyp EmailTyp
        {
            get { return emailTyp; }
            set { emailTyp = value; OnPropertyChanged(); }
        }

        private List<MyApp.Entities.EmailTyp> emailTypList;
        public List<MyApp.Entities.EmailTyp> EmailTypList
        {
            get { return emailTypList; }
            set { emailTypList = value; OnPropertyChanged(); }
        }
        
        public ICommand Save { get; set; }

        public EmailEditViewModel(MyApp.Entities.Email email)
        {
            this.email = email;
			
            Adresse = email.Adresse;
            EmailTyp = email.EmailTyp;

            // Liste der Typen, z.B. Arbeit, Privat, Sonstige, ...
            EmailTypList = EmailTypService.GetAll();

            Save = new RelayCommand(SaveExecute, IsValid);
            
            RegisterValidator(() => Adresse, ValidateAdresse);
            ValidateAll();
        }

        protected async Task<string> ValidateAdresse()
        {
            return await EmailValidationRule.ValidateEmail(adresse);
        }

        protected override void UpdateDataModel()
        {
            email.EmailTyp = emailTyp;
            email.Adresse = adresse;
        }

        protected override void SaveExecute(object obj)
        {
            UpdateDataModel();
            EmailService.Save(email);
            Window.GetWindow(obj as DependencyObject).Close();
        }
    }
}

Service + Entitäten:


namespace MyApp.Service
{
    public class EmailService
    {
        public static void Save(MyApp.Entities.Email email)
        {
            using (var context = new MyAppEntities())
            {
                if (email.EmailTyp != null)
                {
                    if (email.EmailTyp.Id > 0)
                    {
                        // Nur den FK ohne referenziertes Objekt verwenden, um keine Duplikate beim Speichern zu erzeugen
                        email.EmailTypId = email.EmailTyp.Id;
                        email.EmailTyp = null;
                    }
                    else
                    {
                        context.Entry(email.EmailTyp).State = EntityState.Added;
                    }
                }

                context.Entry(email).State = email.Id == 0 ? EntityState.Added : EntityState.Modified;
                context.SaveChanges();
            }
        }
    }
}

namespace MyApp.Entities
{
    public partial class Email
    {
        public int Id { get; set; }
        public Nullable<int> EmailTypId { get; set; }
        
        public virtual EmailTyp EmailTyp { get; set; }
    }
    
    public partial class EmailTyp
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
}
Dieser Beitrag wurde 2 mal editiert, zum letzten Mal von sugar76 am .
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 15618
Herkunft: BW

beantworten | zitieren | melden

Statische Methoden sind weder modular noch testbar.
[Artikel] Unit-Tests: Einführung in das Unit-Testing mit VisualStudio

Ansonsten gilt auch hier
[Artikel] Drei-Schichten-Architektur

Die Entität sollte auch EMailAddress heissen; denn es ist eine E-Mail Adresse und keine E-Mail.
Auch der Name des EMailService macht wenig sinn, wenn es offensichtlich eher ein Repository ist; daher wäre wohl EMailAddressMssqlRepository mit einem IEMailAddressRepository Interface eher passend.

Warum ist der Typ bei Dir eine extra Klasse und nicht einfach ein ENum, wenn es eh nur zwei Typen gibt und offensichtlich keine Erweiterbarkeit notwendig ist?
Asynchrone Methoden sollten auch einen Async-Suffix haben.

Da es eine Review Frage ist, auch dahin verschoben.
private Nachricht | Beiträge des Benutzers
sugar76
myCSharp.de - Member



Dabei seit:
Beiträge: 66
Herkunft: Berlin

Themenstarter:

beantworten | zitieren | melden

Zitat von Abt
Statische Methoden sind weder modular noch testbar.
Ja, das steht noch auf meiner Liste ...
Zitat von Abt
Ansonsten gilt auch hier
[Artikel] Drei-Schichten-Architektur
Die drei Schichten sind in dem Beispiel korrekt abgebildet, meine ich, oder? Bei MVVM übernimmt das ViewModel ja Aufgaben des Controllers (sprich: die Verbindung zwischen View und Datenmodell).

Ansonsten danke für die Hinweise zum Naming. Werde das berücksichtigen.
private Nachricht | Beiträge des Benutzers
sugar76
myCSharp.de - Member



Dabei seit:
Beiträge: 66
Herkunft: Berlin

Themenstarter:

beantworten | zitieren | melden

Nachtrag: mich würde noch interessieren, wie die Daten aus dem ViewModel am besten an die Datenzugriffsschicht übergeben werden ...

Die ganzen Tutorials zum Thema MVVM/EF sind meist extrem einfach gehalten, kennt da jemand vielleicht ein Real-World-Beispiel?

EDIT: nachdem ich das geschrieben habe, hab ich noch mal gesucht und tatsächlich ein gutes Tutorial gefunden: https://blog.magnusmontin.net/2013/05/30/generic-dal-using-entity-framework/
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von sugar76 am .
private Nachricht | Beiträge des Benutzers