Laden...

WPF Projekt mit Dependency Injection

Letzter Beitrag vor 11 Monaten 28 Posts 1.095 Views
WPF Projekt mit Dependency Injection

Hallo,

ich möchte einen Code mit einem MainWindow, MainWindowViewmodel und einem UserService schreiben. In der View gibt es eine TextBox, ein Button und ein Itemcontrol.

<Window x:Class="InversionOfControl.MainWindow"
       ...>
   <StackPanel Margin="20">
       <TextBox Text="{Binding InputName}" Margin="5"></TextBox>
       <Button Command="{Binding AddNameButton}" Margin="5">Next ViewModel</Button>
       <ItemsControl MinHeight="100" ItemsSource="{Binding Usernames}" BorderBrush="Black" BorderThickness="1" Margin="5">
           <ItemsControl.ItemTemplate>
               <DataTemplate>
                   <TextBlock Text="{Binding}"></TextBlock>
               </DataTemplate>
           </ItemsControl.ItemTemplate>
       </ItemsControl>
   </StackPanel>
</Window>

In der View kann ein Name eingetippt werden und mit einem Klick auf den Button, wird eine Methode von UserService aufgerufen, die den Namen in die ObservableCollection aus dem ViewModel speichert. Ja das geht auch ohne UserService, aber für meine eigentliche Anwendung brauche ich eine derartige Konstellation, also ein Command aus dem Viewmodel, öffnet eine Methode eines Servcie und dieser Service ändert properties im ViewModel. Oder auch ein Viewmodel wird von einem anderen ausgerufen, und das gleiche Beispiele (Hierbei ist das . Viewmodel für einen kleinen Bereich der View verantwortlich und wenn Controls in diesem Bereich genutzt werden, ändern sich auch andere Bereiche).

So wie ich es versuchen will, geht das mit "new", denn so kann ich Referenzen wild hin und her geben. Aber ich möchte etwas besser programmieren und die IoC mit DI anwenden. Aber ich glaube ich mache da prinizipiell was falsch. Daher habe ich dieselbe Methode jetzt zweimal implementiert, um von der "altbewerten" Programmierart auf die etwas modernere umzusteigen, aber ich kriege es irgendwie nicht hin (Ich erhalte immer eine Meldung: A circular dependency was detected)

Meine Klassen ohne DI:

public partial class MainWindow : Window
{
   public MainWindow()
   {
       InitializeComponent();
       this.DataContext = new MainWindowViewModel();
   }
}
    public class MainWindowViewModel : ObservableObject
   {
       public ObservableCollection<string> Usernames { get; private set; } = new ObservableCollection<string>();
       
       public RelayCommand AddNameButton { get; set; }
       
       private string _inputName;
       public string InputName
       {
           get { return _inputName; }
           set
           {
               _inputName = value;
               OnPropertyChanged();
           }
       }
       
       private UserService _userService = new UserService();
       
       public MainWindowViewModel()
       {
           AddNameButton = new RelayCommand(() =>
           {
               _userService.GetUsers(this);
           });
       }
  }
public ObservableCollection<string> Usernames { get; private set; } = new ObservableCollection<string>();
      public void GetUsers(MainWindowViewModel mainWindowViewModel)
      {
          mainWindowViewModel.Usernames.Clear();
          Usernames.Add(mainWindowViewModel.InputName);
          foreach (var username in Usernames)
          {
              mainWindowViewModel.Usernames.Add(username);
          }
      }
  }

Meine Klassen mit DI:

        public App()
       {
           Ioc.Default.ConfigureServices(
           
               new ServiceCollection()
                   .AddSingleton<MainWindow>()
                   .AddSingleton<MainWindowViewModel>()
                   .AddTransient<IUserService, UserService>()
                   
                   .BuildServiceProvider() );

           MainWindow mainWindow = Ioc.Default.GetRequiredService<MainWindow>();
           mainWindow.Show();
       }
        public MainWindow(IUserService iUserService, MainWindowViewModel mainWindowViewModel)
       {
           InitializeComponent();
           this.DataContext = mainWindowViewModel;
       }
        public ObservableCollection<string> Usernames { get; private set; } = new ObservableCollection<string>();

       public RelayCommand AddNameButton { get; set; }
       
       private string _inputName;
       public string InputName
       {
           get { return _inputName; }
           set
           {
               _inputName = value;
               OnPropertyChanged();
           }
       }

       public MainWindowViewModel(IUserService userService)
       {
           AddNameButton = new RelayCommand(() =>
           {
               userService.GetUsers();
           });
       }
   }
        public ObservableCollection<string> Usernames { get; private set; } = new ObservableCollection<string>();
       MainWindowViewModel _mainWindowViewModel;
       public UserService(MainWindowViewModel mainWindowViewModel)
       {
           _mainWindowViewModel = mainWindowViewModel;
       }
       public void GetUsers()
       {
           _mainWindowViewModel.Usernames.Clear();
           Usernames.Add(_mainWindowViewModel.InputName);
           foreach (var username in Usernames)
           {
               _mainWindowViewModel.Usernames.Add(username);
           }
       }
   }

A circular dependency was detected bedeutet, dass zwei Abhängigkeiten sich gegenseitig kennen, was nicht sein darf.

public class Hase
{
    public Hase(Igel ..) {..}
}

public class Igel
{
    public Igel(Hase ..) {..}
}
  • Hase kann nicht erstellt werden, ohne Igel zu übergeben
  • Igel kann nicht erstellt werden, ohne Hase zu übergeben
  • => circular dependency

Und genau das passiert bei Dir:

public UserService(MainWindowViewModel mainWindowViewModel)
public MainWindowViewModel(IUserService userService)

Das ist nicht nur eine Dependency-Verletzung, sondern auch eine Schichtverletzung ⇒ [Artikel] Drei-Schichten-Architektur
Deine Logik darf niemals etwas von der UI kennen - hier übergibst Du aber ein ViewModel: das ist Käse.


Deine Implementierung vom UserService ist auch eher so suboptimal.

Die Methode heisst public void GetUsers() - aber Du gibst gar keine User zurück. Du füllst irgendeine ViewModel-Instanz.
Gib einfach die Liste von User zurück. Das ViewModel ist völlig irrelevant für die Logik.

Servus,

wenn ich alles richtig verstanden habe, dann vielleicht so (ungetestet)

public App()
{
    Ioc.Default.ConfigureServices(

        new ServiceCollection()
            .AddSingleton<MainWindow>()
            .AddSingleton<MainWindowViewModel>()
            .AddTransient<IUserService, UserService>()

            .BuildServiceProvider() );

    MainWindow mainWindow = Ioc.Default.GetRequiredService<MainWindow>();
	mainWindow.DataContext = Ioc.Default.GetRequiredService<MainWindowViewModel>();
    mainWindow.Show();
}
public MainWindow()
{
    InitializeComponent();
}
public ObservableCollection<string> Usernames { get; private set; } = new ObservableCollection<string>();

    public RelayCommand AddNameButton { get; set; }
    private readonly IUserService userService;
	
    private string _inputName;
    public string InputName
    {
        get { return _inputName; }
        set
        {
            _inputName = value;
            OnPropertyChanged();
        }
    }

    public MainWindowViewModel(IUserService userService)
    {
	    this.userService = userService;
        AddNameButton = new RelayCommand(() =>
        {
		    Usernames.Clear();
            Usernames = userService.GetUsers(InputName);
        });
    }
}
    public ObservableCollection<string> Usernames { get; private set; } = new ObservableCollection<string>();

    public UserService()
    {
    }
	
    public ObservableCollection<string> GetUsers(string name)
    {
        Usernames.Add(name);
        return Usernames;
    }
}

Das ist ja eine lustige Logik in dem Programm. Auf jeden Fall ist die Userliste immer sehr schön aufgeräumt.

Hat die Blume einen Knick, war der Schmetterling zu dick.

ObservableCollection<string> GetUsers(string name)

Auch eher nich so doll, weil Du damit Verantwortung abgibst.
Es spricht nichts gegen eine List<T> als return und das ViewModel kann sich selbst um seine ObservableCollection kümmern.

Servus Caveman

Das sieht doch schonmal ganz gut aus. Von mir noch ein paar Anmerkungen.

ObservableCollection verwende ich eher im UI-Context. Wurde aber schon erwähnt. Die ObservableCollection kann dann im ViewModel gefüllt werden. Je nach Komplexität kann der User dann noch in ein UserViewModel konvertiert werden.

Erstellen und Anzeigen vom Window mache ich nicht im App-Konstruktor, sondern:

    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            
            // hier
        }
    }

Du hast View und ViewModel als Singleton erstellt. Sofern es dafür keine speziellen Gründe gibt, würde ich die transient machen. ViewModel würde ich generell nicht als Singleton machen, sonst hast du auf einmal Daten auf der View, die da nicht sein sollten.

Gruss
Alf

Hallo,

das handhabe ich auch so.

Grundsätzlich wollte ich nicht zu viel ändern, weil

  1. ich zu faul war noch mehr im Notepad++ zu schreiben.
  2. der Code noch irgendwelche Ähnlichkeit zum Ursprungscode haben sollte.

Wpf.Extensions.Hosting

// Create a builder by specifying the application and main window.
var builder = WpfApplication<App, MainWindow>.CreateBuilder(args);

// Configure dependency injection.
// Injecting MainWindowViewModel into MainWindow.
builder.Services.AddTransient<MainWindowViewModel>();

// Configure the settings.
// Injecting IOptions<MySettings> from appsetting.json.
builder.Services.Configure<MySettings>(builder.Configuration.GetSection("MySettings"));

// Configure logging.
// Using the diagnostic logging library Serilog.
builder.Host.UseSerilog((hostingContext, services, loggerConfiguration) => loggerConfiguration
    .ReadFrom.Configuration(hostingContext.Configuration)
    .Enrich.FromLogContext()
    .WriteTo.Debug()
    .WriteTo.File(
        @"Logs\log.txt", 
        rollingInterval: RollingInterval.Day));
    
var app = builder.Build();

await app.RunAsync();

Hat die Blume einen Knick, war der Schmetterling zu dick.

Erstmal Danke für die ganzen Nachrichten

Also wenn ich das richtig verstehe...

Ich habe 3 Bereiche in der GUI. Bereich 1 enthält eine ListView. Bereich 2 enthält UI Elemente, die die ListView mit Inhalt füllen. Bereich 3 Enthält ebenfalls UI Elemente, die die Listview ergänzend mit anderem Inhalt füllen können oder komplett ersetzen können.

Es wird ein Service erstellt also zB. ListViewService, der gewisse methoden hat, die eine ListView mit dem jeweiligen Inhalt befüllen. In dem Service soll nicht die observable Collection enthalten sein. Die soll jedesmal im ViewModel aus der Liste erstellt werden? Aber so klappt das irgendwie nicht, denn, wenn die View aus Bereich 1 nur mit einer observable Collection aus dem Viewmodel 1 gebunden ist und ich in Bereich 2 ListViewService aufrufe und dort lokale Variablen ändere, merkt die View das nicht.

Daher hatte ich jetzt gedacht, dass der ListViewService die observable Collection enthält und in Bereich 1, 2 und 3 muss dieser Service nur noch injecten werden. An die View 1 aus Bereich 1 (UserControl) ist die observable Collection aus dem ListViewService gebunden. Dann ist die Manipulation der ListView abgekapselt in einer Klasse und diese wird einseitig von den Viewmodels aufgerufen. Wenn ich in Viewmodel 1, 2 oder 3 die Methoden aus ListViewService  aufrufe, dann wird die View immer angepasst.

Wäre das so sinnig?

Dann wäre meine Frage. Der ListviewService würde auch die Businesslogik enthalten. Aber die BusinessLogik sollte eigentlich nicht die observable Collection enthalten oder?

Hier bin ich etwas verwirrt.

Wäre es dann sinnvoll ein ViewModel, ein ListViewViewModelService und eine ListViewBusinseslogik zu erstellen?

Und die Klassen werden derartig injected:

ViewModel → ListViewViewModelService  → ListViewBusinseslogik

Es wird ein Service erstellt also zB. ListViewService, der gewisse methoden hat, die eine ListView mit dem jeweiligen Inhalt befüllen. In dem Service soll nicht die observable Collection enthalten sein. Die soll jedesmal im ViewModel aus der Liste erstellt werden? Aber so klappt das irgendwie nicht, denn, wenn die View aus Bereich 1 nur mit einer observable Collection aus dem Viewmodel 1 gebunden ist und ich in Bereich 2 ListViewService aufrufe und dort lokale Variablen ändere, merkt die View das nicht.

Es ist nicht üblich, dass man sich eigene Service für UI Elemente baut. "Service" ist eine Begrifflichkeit aus der Logik-Architektur.
.NET Naming Best Practises: Services vs. Providers

Dann wäre meine Frage. Der ListviewService würde auch die Businesslogik enthalten. Aber die BusinessLogik sollte eigentlich nicht die observable Collection enthalten oder?

Nein, natürlich nicht. Dein UI Element soll und darf keine Business Logik enthalten - zumindest wenn Du eine saubere Architektur willst.
Business-Logik Bausteine sollen unabhängig von der UI sein, sodass Du diese Klassen in mehreren Projekten nutzen kannst. Wenn Du irgendeine Abhängigkeit in Deiner Logik zB zu WPF hast, dann kannst Du diese Klasse zB nicht mit Windows Forms verwenden.

[Artikel] Drei-Schichten-Architektur

Business Logik bezieht sich immer auf Business Modelle (User, Orders...) und nicht auf UI Elemente.


Technische Limitation: Deine ObservableCollection darf nicht neu erzeugt werden.

Das ViewModel erzeugt diese Liste in der Initialisierung (am besten direkt an der Property) und muss dann dauerhaft bestehen bleiben.
Du darst die Referenz nicht verändern. Willst Du Änderungen an der Liste vornehmen, dann musst Du sie leeren und neue Items hinzufügen - aber nicht neu erzeugen, sonst hast auch eine neue Referenz.

Daher ist es auch nicht sinnvoll, dass irgendeine andere Klasse eine neu erzeugte ObservableCollection zurück gibt.

Wenn die Oberservable Collection in dem ViewModel bleibt, was mache ich dann in dem eben beschriebenen Fall der 3 ViewModels?

ViewModel 1 Enthält die observable Collection, des Listviews, aber in ViewModel 2 und 3 (Für Bereiche 2 & 3) sind Controls, die diese ListView ändern. Soll und womöglich gibt es in Bereich 1 auch Elemente, die Inhalte von Bereich 2 und 3 ändern. Dann müsste ich Referenzen hin und her geben und habe diese circular dependency. Mit dem exportieren der observable Collection und aller weiteren Bindings der 3 ViewModels, kann ich von den 3 ViewModels aus jedes gebundene GUI Element verändern.

Wie würde man das denn anders implementieren?

Du könntest ein Haupt-ViewModel benutzen, dass die anderen ViewModels hält und mit Daten füttert.
Ich habe dir mal ein minimales Beispiel angehängt.

In einfachen Anwendungen würde man das machen wie Alf Ator es sagt, in komplexeren Anwendungen wird sowas nicht geteilt. Es werden auch keine Referenzen hin und her kopiert.

Man würde in größeren Anwendungen auf ein Messaging System setzen, zB Reactive Extensions oder ReactiveUI.
Das heisst jede View ist total unabhängig, kennt keine anderen Views und View-übergreifende Daten, Events und Informationen werden über Subscriptions bekannt gemacht und in den Views aktualisiert.

https://github.com/dotnet/reactive

Zitat von Abt

Technische Limitation: Deine ObservableCollection darf nicht neu erzeugt werden.

Diese Aussage verstehe ich nicht, denn ich hatte noch niemals Probleme damit so eine ObservableCollection neu zu erstellen.

Kannst du das bitte einmal erläutern?

Hat die Blume einen Knick, war der Schmetterling zu dick.

Das verstehe ich auch nicht, da ein OnPropertyChanged(nameof(...))die UI antriggert, die ObservableCollection wieder neu einzulesen (d.h. den Getter der Eigenschaft aufzurufen).

Persönlich lösche ich aber auch immer diese Collection und fülle sie dann. Dafür habe ich extra folgende Extension-Methoden erzeugt:

namespace MVVM_Library
{
	public static class Extensions
	{
		public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> items)
		{
			foreach (var item in items)
				collection.Add(item);
		}

		public static void SetRange<T>(this ICollection<T> collection, IEnumerable<T> items)
		{
			collection.Clear();
			collection.AddRange(items);
		}
	}
}

OnPropertyChanged ist der Framework-Mechanismus, wenn Du Properties hast, die einen "Dirty State" haben, der eigentlich optimalerweise zu vermeiden ist. Damit triggerst Du WPF an, dass die Referenz neu bekannt gemacht wird und die Value wird aktualisiert.

Er lässt sich bei den Typen vermeiden, die eine direkte Möglichkeit der Bindung haben; wie die ObservableCollection.

Diese Aussage verstehe ich nicht, denn ich hatte noch niemals Probleme damit so eine ObservableCollection neu zu erstellen.

Doch hattest Du, denn die Bindung in diesem Moment ist nicht mehr vorhanden, sie zeigt auf die alte Referenz.
Du wirst sicherlich eben auch OnPropertyChanged triggern, um das zu lösen.


Persönlich lösche ich aber auch immer diese Collection und fülle sie dann.

Die generelle Empfehlung ist in WPF, genau das eigentlich nicht zu tun, weil der Overhead von OnPropertyChanged,der Allocation und die Bindungsaktualisierung i.d.R. teurer ist als das Clear, das O(n) ist.

Eine Neu-Initialisiert ist nur dann empfohlen, wenn Du sehr viele Items hast und die ObservableCollection direkt mit den Items initialisierst.

Und die vielen Themen dazu in diesem Forum, aber auch zB bei SO zeigen, dass das den Leuten einfach nicht bewusst ist.

Zitat von Abt

OnPropertyChanged ist der Framework-Mechanismus, wenn Du Properties hast, die einen "Dirty State" haben, der eigentlich optimalerweise zu vermeiden ist. Damit triggerst Du WPF an, dass die Referenz neu bekannt gemacht wird und die Value wird aktualisiert.

Er lässt sich bei den Typen vermeiden, die eine direkte Möglichkeit der Bindung haben; wie die ObservableCollection.

Diese Aussage verstehe ich nicht, denn ich hatte noch niemals Probleme damit so eine ObservableCollection neu zu erstellen.

Doch hattest Du, denn die Bindung in diesem Moment ist nicht mehr vorhanden, sie zeigt auf die alte Referenz.
Du wirst sicherlich eben auch OnPropertyChanged triggern, um das zu lösen.

Also ich habe speziell diese "Technische Limitation" nicht verstanden, da es einfach nur eine observable Property braucht wie halt bei jeder Property, deren Wert sich ändern kann und diese Änderungs Benachrichtigung benötigt wird.

Hier von einem Problem zu sprechen?

Ich kann versichern, dass ich da genau kein Problem hatte/habe/haben werde.

Hat die Blume einen Knick, war der Schmetterling zu dick.

Zitat von Abt

Persönlich lösche ich aber auch immer diese Collection und fülle sie dann.

Die generelle Empfehlung ist in WPF, genau das eigentlich nicht zu tun, weil der Overhead von OnPropertyChanged,der Allocation und die Bindungsaktualisierung i.d.R. teurer ist als das Clear, das O(n) ist.

Er meinte damit "lösche den Inhalt" - sieht man auch wenn man seinen Code dazu anschaut 😉 Also genau so wie empfohlen.

Hat die Blume einen Knick, war der Schmetterling zu dick.

Danke an alle, dass muss ich mir am Wochenende mal im Detail ansehen.

Zitat von Alf Ator

Du könntest ein Haupt-ViewModel benutzen, dass die anderen ViewModels hält und mit Daten füttert.
Ich habe dir mal ein minimales Beispiel angehängt.

Ich hatte jetzt doch lust etwas wenig zu schlafen und mich daran zu setzen^^ Dein Beispiel ist doch dahingegen anders als mein Fall, da ich mehrere Views habe, es ist ähnlich zu dem Fall bei dem Verlinkten anderen Beitrag.

Ich sehe mir das mit den Messengern mal an, aber ich verstehe die Aufteilung von MVVM wohl nicht 100%. Daher mal ein anderes Beispiel. Ich habe ein Projekt, da möchte ich in der View editierbare Textboxen haben. Der Inhalt der Textboxen stammt defaultmäßig aus xml dateien und kann in der View beschrieben und in die xml Datei geladen werden. Die Textboxen enthalten lediglich Zahlen. Die View zeigt zu den Zahlen einen Text. Text zu Zahl die Information stammt aus einer DB.

Jetzt frage ich mich ok, Text zu Zahl aus der DB ist Teil des Models/Businessmodels, Laden des Inhalts der Zahlen aus einer Datei und Speichern in eine Datei auch auch? Was passiert dann im ViewModel, werden dort nur die Klassen DB und File laden als Objekte erstellt und die Methoden genutzt + Button Commands und Alle Binding Variablen? Versuche ich soviel in Modell zu legen, wie geht? Die Methoden des Modells spucken als return values dann strings oder listen aus und diese werden im Viewmodell an die Binding variablen übergeben?

Sehe ich das so korrekt?

Zitat von CSharpNewbie2022

Zitat von Alf Ator

Du könntest ein Haupt-ViewModel benutzen, dass die anderen ViewModels hält und mit Daten füttert.
Ich habe dir mal ein minimales Beispiel angehängt.

Ich hatte jetzt doch lust etwas wenig zu schlafen und mich daran zu setzen Dein Beispiel ist doch dahingegen anders als mein Fall, da ich mehrere Views habe, es ist ähnlich zu dem Fall bei dem Verlinkten anderen Beitrag.

Das Beispiel lässt sich doch supereinfach auf deinen Fall erweitern. Ich dachte das reicht aus.

Jetzt frage ich mich ok, Text zu Zahl aus der DB ist Teil des Models/Businessmodels, Laden des Inhalts der Zahlen aus einer Datei und Speichern in eine Datei auch auch?

Ja. Schau dir dazu das Repository-Pattern an.

Was passiert dann im ViewModel, werden dort nur die Klassen DB und File laden als Objekte erstellt und die Methoden genutzt + Button Commands und Alle Binding Variablen?

Ja.

Versuche ich soviel in Modell zu legen, wie geht?

Es geht darum, Geschäftslogic und UI(-Logic) voneinander zu trennen.

Die Methoden des Modells spucken als return values dann strings oder listen aus und diese werden im Viewmodell an die Binding variablen übergeben?

Ja.

Ich hab das Beispiel etwas erweiter.

Hallo Alf,

erstmal danke dir für deine Zeit.

Ich habe mir jetzt verschiedenste Dinge angesehen und mir alle Beiträge durchgelesen (nicht alles im Detail verstanden), aber ich glaube, dass die Messenger genau das sind, was ich suche. Nichtsdestotrotz sollte es doch auch ohne gehen oder´? Ich glaube, dass deine Lösung immer noch nicht mein Anliegen löst. Daher habe ich ein weiteres Beispiel. Mit dieser Problemstellung kann ich einfach kein DI nutzen. Mit new würde es klappen, daher habe ich das Ziel Projekt ohne DI und ein unfertiges Wunschprojekt mit DI angehangen.

Also ganz kleines WPF Projekt mit 4 Grid Zeilen im MainWindow Fenster - gebunden an MainWindowViewModel:

Zeile 0: TextBlock mit Hintergrund farbe (Rot oder Grün)

Zeile 1: ListView (Gebunden an LiestViewItems property in ListViewViewModel mit UserControl ListView)

Zeile 2: Eingabe TextBox: Inhalt gebunden an TextBoxContent property im MainWindowViewModel

Zeile 3: Add und DeleteButton beide gebunden an jeweils 1 buttonCommand im MainWindowViewModel. Beide Commands manipulieren LiestViewItems property aus dem UserControl (Bis hierhin klappt es mit dem MultiViewModel). Sobald LiestViewItems property mehr als 5 Elemente hat, wird das TextBlock aus Zeile 0 (gebunden auf property im MainWindowViewModel) rot.

Mit new klappt auch das, weil ich ja beim erstellen von ListViewViewModel auch die Referenz mit This MainWindowViewModel übergeben kann. Aber mit dem DI kriege ich das nicht hin. Hat da einer eine Idee?

Servus,

im angehängten Projekt sollte das mit DI klappen.

Also erstmal danke ich nochmal allen. Das hat mir echt weitergeholfen.

Ich habe jetzt das mit den MultiViewmodels verstanden, aber nutze doch die Messenger, weil die echt praktisch sind.

Jetzt entwickle ich alle Viewmodels unabhängig voneinander und kommuniziere bei Bedarf mit Messengern.

Jetzt habe ich eigentlcih aus dem allen nur noch 2 Fragen, die mir etwas Unklarheit geschaffen haben.

  1. Bei diesem Schichten Modell trennen wir GUI, Business Logic and Data Access. Das habe ich so adaptiert. Als Data Access wäre der Zugriff zur Datenbank und jegliche Art von Lesen und Beschreiben von Dateien. Aber wie sieht es ..
    1. mit Schnittstellen (Ethernet, Ethercat, SPI, RS232, ...)
    2. mit Webdiensten also Kommunikation zu Api s aus, gehören diese Elemente auch in den Data Access Layer?

Diese Dinger unterscheiden sich zu DB und Dateien Zugriff darin, dass mit einem externen Partner kommuniziert wird, also gehört das nun zur Business Logic oder zu dem Data Access Layer?

2. Ich nutze den DI Container von Microsoft für alle ViewModels und diverse Singleton Klassen aus der Business Logic, damit diese in der GUI Schicht zur Verfügung stehen. Aber kleine Klassen, die nur innerhalb einer Klasse verwendet werden, erstelle ich immernoch mit new. Ist das eher gegen das Konzept? Sollte jedes Object mit dem DI erstellt werden?

Zitat von CSharpNewbie2022

2. Ich nutze den DI Container von Microsoft für alle ViewModels und diverse Singleton Klassen aus der Business Logic, damit diese in der GUI Schicht zur Verfügung stehen. Aber kleine Klassen, die nur innerhalb einer Klasse verwendet werden, erstelle ich immernoch mit new. Ist das eher gegen das Konzept? Sollte jedes Object mit dem DI erstellt werden?

Mal eine vereinfachte Darstellung:

  • Alle Klassen, die für andere Klassen Abhängigkeiten (Dependencies) darstellen sowie die, die eben solche Abhängigkeiten (Dependencies) haben werden registriert und über den DI-Container erzeugt.
  • Die anderen Klassen ... wozu?

Das ViewModel ist abhängig von einem DatenService und dieser ist abhängig von einem DbContext also werden diese alle am DI-Container registriert und von diesem erzeugt.

Das ViewModel schickt jetzt einen Datensatz zum speichern an den DatenService und der schickt das an den DbContext weiter. Dieser Datensatz (ist eine Klasse) hat keinerlei Abhängigkeiten (Dependencies) zu irgendwas und hat somit nichts mit Dependency-Injection am Hut.

Hat die Blume einen Knick, war der Schmetterling zu dick.

Zitat von CSharpNewbie2022

  1. Bei diesem Schichten Modell trennen wir GUI, Business Logic and Data Access. Das habe ich so adaptiert. Als Data Access wäre der Zugriff zur Datenbank und jegliche Art von Lesen und Beschreiben von Dateien. Aber wie sieht es ..
    1. mit Schnittstellen (Ethernet, Ethercat, SPI, RS232, ...)
    2. mit Webdiensten also Kommunikation zu Api s aus, gehören diese Elemente auch in den Data Access Layer?

Diese Dinger unterscheiden sich zu DB und Dateien Zugriff darin, dass mit einem externen Partner kommuniziert wird, also gehört das nun zur Business Logic oder zu dem Data Access Layer?

Alles was nicht in deiner Anwendung ist, ist außen/extern und du baust dir für deine Anwendung eine Schnittstelle um mit dieser Außenwelt zu kommunizieren. Du darfst das gerne unter dem Begriff Infrastructure zusammenfassen.

Hat die Blume einen Knick, war der Schmetterling zu dick.

Hallo CSharpNewbie2022,

der Name Datenzugriffsschicht (Data Access Layer) bezieht sich ja gerade auf das Kommunizieren mit externen Komponenten, um Daten zu erhalten (lesen) und zu senden (schreiben). Der Geschäftslogik ist es dann egal, woher genau und mit welcher technologischen Umsetzung diese Daten kommen (bzw. gesendet werden), so daß diese Komponenten einfach(er) ausgetauscht werden können (für das gleiche Projekt, falls sich die Anforderungen oder Infrastruktur geändert hat oder auch für andere, ähnliche auf der Geschäftslogik aufgebaute Projekte).

Du kannst dir auch mal die Zwiebel-Architektur anschauen, z.B. DDD mit Onion Architecture. Dort fallen dann alle externen Zugriffe unter die Infrastruktur, d.h. der äußere Kreis. Und der innere Kreis ist dann die, auf Zeit langlebig gedachte, stabile Geschäftslogik.