Laden...

Wie trennt man Verantwortlichkeiten in WPF?

Erstellt von Spooner8 vor 2 Jahren Letzter Beitrag vor 2 Jahren 2.041 Views
2.079 Beiträge seit 2012
vor 2 Jahren

Warum muss die CalculateAreas-Methode denn im AddonsViewModel sein?
Leg sie ins ItemViewModel und Du hast auf alles Zugriff was Du brauchst.

Oder reich das ItemViewModel zum AddonsViewModel durch.
Wenn dir das nicht gefällt (was ich nachvollziehen kann), kannst Du auch eine Abstraktion (Interface) dazwischen legen, dann arbeitest Du beim Berechnen mit dem Interface und das ItemViewModel implementiert dieses Interface.

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.

S
Spooner8 Themenstarter:in
40 Beiträge seit 2020
vor 2 Jahren

Die CalculateAreas-Methode berechnet eine Fläche von einer Eigenschaft vom AddonViewModel.
z.B. hat das AddonViewModel die Eigschaften Links, Rechts, Oben, Unten, Dicke Links, Dicke Rechts....
Es hat also 8 Eigeschaften und ca. 3 Methoden die greifen, sobald sich einer der Eigeschaften ändert. Für ide Berechnung wird dann eine Eigenschaft vom DimensionViewModel benötigt die wiederum diverse Eigenschaften wie Breite, Höhe, ElementGesamtbreite, GesamtHöhe und so weiter beinhaltet und auch diese hat Methoden die wieder andere Berechnungen anstellen, sofern eine Eigenschaft sich ändert.
Ich habe diese beiden Bereiche beim Neuaufba extra extrahiert weil die ItemViewModel - Klasse vorher so enorm gross war und ich es übersichtlicher machen musste/wollte, wie du ja auch angemerkt hattest. Macht auch Sinn zwecks Übersicht.

Da diese beiden SubViewModels jedoch eigenständige Bereiche sind und nicht eine Ableitung von ItemViewModel ist ein Interface hier nicht der richtige Weg. Es würde dann ja wieder dazu führen, dass im ItemViewModel enorm viel implementiert sein muss und da dieses Programm speziell für genau diese eine Art von Item erstellt wird, wäre der Nutzen eines Interfaces nicht vorhanden.
Da muss ich wohl so weiter machen wie bis anhin und einfach das ItemViewModel oder die benötigte Eigenschaft davon jeweils übergeben mit dem Konstruktor der SubViewModels.
Ich wollte nur gefragt haben ob ich aus der Subklasse nicht ganz einfach bei Bedarf die Klasse worin die Subklasse instanziiert wurde abrufen kann um davon die Eigenschaften zu verwenden.

2.079 Beiträge seit 2012
vor 2 Jahren

Da diese beiden SubViewModels jedoch eigenständige Bereiche sind und nicht eine Ableitung von ItemViewModel ist ein Interface hier nicht der richtige Weg. Es würde dann ja wieder dazu führen, dass im ItemViewModel enorm viel implementiert sein muss

Warum? O.o
Du brauchst aktuell das ItemViewModel - was spricht dagegen, dem ItemViewModel ein Interface zu geben, was genau die Methoden definiert, die Du brauchst?

Du könntest natürlich auch einen anderen Weg gehen und die Berechnung ganz raus ziehen. Gerade wenn die Berechnung komplexer ist, kann das durchaus interessant sein.
Du hast dann also eine Klasse (kein ViewModel), die sich nur um die Berechnung kümmert. Die ViewModels füttern sie mit den benötigten Daten und dein AddonViewModel bekommt eine Referenz (ggf. hinter einem Interface) darauf.
Also eine Art AddonCalculationService

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.

S
Spooner8 Themenstarter:in
40 Beiträge seit 2020
vor 2 Jahren

Warum? O.o

Naja ein Interface schreibt ja lediglich vor welche Methoden und Eigenschaften die Klasse (in dem Fall ItemViewModel) haben MUSS. Die Logik wird dann ja trotzdem in der Klasse selbst definiert. Somit ist die ganze Calculation wieder in der Hauptklasse, auch wenn in den SubViewModels etwas ändert. Das wollte ich ja aufteilen um die Klassen kleiner zu haben.

Der Ansatz aber die Methode für die Berechnung generell aus zu lagern ist ein Gedanke wert. Dem Service müsste ich dann tatsächlich nur das ItemViewModel übergeben um alles rechnen zu können und es bekommt stumpf einen oder mehrere Preise zurück. Muss ich mal drüber nach grübeln.

Damit Ordnung herrscht -> Dafür macht man in MVVM dann einen Ordner mit "Services" welche die Klasse wie du schon angedeutet hast z.B. "ItemCalculationService" heissen würde. Ist das so richtig bzw. Ordnungsgemäss versorgt?

Bezüglich Ordner habe ich aktuell bereits einige Commands im Command-Ordner. Macht es Sinn z.B. die Commands "MinimizeCommand", "MaximizeCommand" und "CloseCommand" in eine klasse "BaseViewCommands" zu legen um nicht zu viele Klassen zu bekommen? Also etwas Gruppieren? Oder sollte das nicht gemacht werden?

16.834 Beiträge seit 2008
vor 2 Jahren

Der Ansatz aber die Methode für die Berechnung generell aus zu lagern ist ein Gedanke wert.

Das wäre nicht nur ein Gedanke wert, das wäre der richtige Weg.

Damit Ordnung herrscht -> Dafür macht man in MVVM dann einen Ordner mit "Services" welche die Klasse wie du schon angedeutet hast z.B. "ItemCalculationService" heissen würde. Ist das so richtig bzw. Ordnungsgemäss versorgt?

Das wäre der aller aller eiinfachste Weg, aber keine echte Architektur.
Auch bei Services trennt man zwischen Interfaces und Implementierung, die sich nicht zwangsläufig im gleichen Namespaces oder gar im gleichen Projekt befinden müssen / sollen - je nachdem.
Services übernehmen Logik aller Art: Kalkulationen, Orchestrierung von Providern, Repositories, Clients....

Nur dann wäre auch Deine Software überhaupt testbar.
[Artikel] Unit-Tests: Einführung in das Unit-Testing mit VisualStudio

Bezüglich Ordner habe ich aktuell bereits einige Commands im Command-Ordner.

Denke in Aufgaben und Verantwortungen, nicht in Ordnern. Darauf basiert das gesame Namespacing in .NET.
Namen von Namespaces - Framework Design Guidelines

In Mini-Tools mag das noch so funktionieren, aber man erkennt anhand von Namespaces nicht zwangläufig ein echtes Software Design.
Siehe zB Martin Fowler - domain driven design

4.939 Beiträge seit 2008
vor 2 Jahren

Der Ansatz aber die Methode für die Berechnung generell aus zu lagern ist ein Gedanke wert. Dem Service müsste ich dann tatsächlich nur das ItemViewModel übergeben um alles rechnen zu können und es bekommt stumpf einen oder mehrere Preise zurück. Muss ich mal drüber nach grübeln.

Die Abhängigkeit sollte aber andersherum sein: die Service-Klasse sollte eigenständig sein und die ViewModels nutzen deren Funktionalität.
Das ganze Projekt sollte immer eindeutig hierarchisch aufgebaut sein: UI -> Logik -> Datenzugriff (DAL), s.a. [Artikel] Drei-Schichten-Architektur.
Und auch innerhalb der UI-Schicht sollte es strikt hierarchisch sein: View -> ViewModel [-> Services] -> Model (Logik).

S
Spooner8 Themenstarter:in
40 Beiträge seit 2020
vor 2 Jahren

Das wäre der aller aller eiinfachste Weg, aber keine echte Architektur.

Das ist mir klar und da nehme ich eure Inputs sehr gerne mit. Ich wollte nur bezüglich Nameing und Ordnerablage sicherheitshalber Rückfragen, damit ich im gesamten Projekt Ordnung habe und nicht nur beim Aufbau der Architektur.

Danke noch für den Link zum Unit-Testing. Das verwenden zu können ist ganz klar die Vorgabe. Darum strenge ich mich an, alles auf Verantwortungen und Aufgaben zu strukturieren.
Das wird mir vielleicht nicht vollständig gelingen, aber ich konnte durch euch schon sehr viel neues lernen. Wenn ich alles korrekt umsetzten kann, um so besser.

Und auch innerhalb der UI-Schicht sollte es strikt hierarchisch sein: View -> ViewModel [-> Services] -> Model (Logik)

Hiess es nicht weiter oben mal, dass die Logik in das ViewModel kommt statt in das Codebehinde der View und die Models nur Klassen mit Eigenschaften sind ohne Logik?
Models und Entities sind sich da doch sehr ähnlich bis gleich. Habe ich das falsch interpretiert gehabt?
In dem Fall würde ich den Service eher als Model anschauen als ein ViewModel.

4.939 Beiträge seit 2008
vor 2 Jahren

Ich habe auch nichts vom Codebehind (der View) geschrieben. Und ein ViewModel kann zwar Logik enthalten, aber es sollte keine Business-Logik (d.h. die eigentliche Kernaufgabe des Programms) sein, sondern nur für die View benötigte Umrechnungslogik - alles andere sollte ausgelagert sein.

Und der Begriff Model ist (ebenso wie Logik) leider mehrdeutig, d.h. es kommt immer auf den konkreten Anwendungsfall an.
Codetechnisch können Models zwar einfach nur Klassen nur mit Eigenschaften (auch POCO oder DTO genannt) sein, jedoch müssen diese ja irgendwo her mit Daten gefüllt sein (und das macht dann eben die Business-Logik). Und für manche Anwendungen macht es dann aber in der UI-Schicht (obwohl gleiches auch für jede andere Schicht gilt) Sinn, daß man zum einfacheren Zugriff den Code dann in Service-Klassen auslagert.

Ergänzung: In deinem konkreten Fall sollte also CalculateAreas ganz klar in die Busisness-Logik und nur mit Klassen (Modellen) arbeiten, welche auch zur Logik-Schicht gehören (und nicht mit ViewModel-Klassen)! Und von dem ViewModel wird dann diese aufgerufen (bzw. über den Umweg einer Service-Klasse - diese macht am meisten Sinn, wenn mehrere ViewModels diese Funktionalität benötigen und so ein einheitlicher Zugriff auf die Logik gewährleistet wird).

S
Spooner8 Themenstarter:in
40 Beiträge seit 2020
vor 2 Jahren

Ok mal sehen ob ich das im Kopf richtig zusammen bekomme.
Mein ItemViewModel sollte also nur die Logik haben wie z.B. Commands und evtl. Events bzw. soll ggf. die Service-Klasse damit beauftragen eine Kalkulation zu starten.
Mein Model hat neben den Eigenschaften dann noch einen oder mehrere Konstruktoren die beim Instanziieren die Eigenschaften anhand von eine Datenbank oder von einem ViewModel ein Item aus der Liste bekommt (beim Kopieren von Items) und kann bei Wertveränderung nicht innerhalb vom Model sondern mit einer Service - Klasse seine Daten aktualisieren.
Stimmt diese Himmelsrichtung so?

Kann man eine Service - Klasse auch grob als Methodenklasse mit statischen Methoden bezeichnen?

Zu deiner Ergänzung: Das müsste ziemlich das sein wie ich oben schreibe. Auf die CalculateAreas Methode muss an sich nur ein SubViewModel zugreifen, aber dieses braucht Zusatzinfos aus dem DimensionsViewModel. Wenn die CalculateAreas Methode nun als ServiceModel (schreibt man dann CalculateAreasServiceModel?) ausgelagert wird und ich die dann nicht vom SubViewModel, sondern vom ItemViewModel aufrufe, kann ich auch alle Infos weiter geben, da das ItemViewModel ja die SubViewModels kennt. Ich glaube so hab ich das Puzzle richtig.

2.079 Beiträge seit 2012
vor 2 Jahren

Mein Model hat neben den Eigenschaften dann noch einen oder mehrere Konstruktoren die beim Instanziieren die Eigenschaften anhand von eine Datenbank oder von einem ViewModel ein Item aus der Liste bekommt

Das klingt, als würde das Model ein ViewModel als Parameter bekommen?

kann bei Wertveränderung nicht innerhalb vom Model sondern mit einer Service - Klasse seine Daten aktualisieren.

Das Model ist am besten unveränderlich, ja.
Es kann aber gewollt sein, dass Änderungen im ViewModel erlaubt sind, z.B. stehen die Änderungen erst im ViewModel, bis auf Speichern gedrückt wird. Der Speichern-Command sammelt dann die Änderungen und lässt sie vom Service speichern.
Z.B. könnte dann der Speichern-Command wieder ein Model zusammen bauen, in dem die ID und die (geänderten) Daten stehen und der Service sucht, validiert und aktualisiert den Datensatz.

Kann man eine Service - Klasse auch grob als Methodenklasse mit statischen Methoden bezeichnen?

Wir sind hier bei OOP und Du willst statisch arbeiten? O.o Nein, natürlich nicht statisch.
Immer wenn Abhängigkeiten zwischen Klassen bestehen, ist alles, was statisch ist, ein Risiko - also eigentlich immer.
Es gibt ein paar Ausnahmen, da nutze ich auch static (z.B. technisch vorgegebene Werte, das sind dann Konstanten), aber das sind Ausnahmen.

Oder Du meinst das "statisch" im übertragenen Sinn, dann s.u. 🙂

Als Beispiel:


public interface IMyService
{
    double GetTotalSurfaceArea(Guid shapeGroupId);
}
public class MyService
{
    // Database connection (or repository) and more dependencies

    public double GetTotalSurfaceArea(Guid shapeGroupId)
    {
        var result = 0;

        foreach (var shape in GetAllShapes(shapeGroupId))
            result += CalculateSurfaceArea(shape);

        return result;
    }

    private IEnumerable<MyShape> GetAllShapes(Guid groupId)
    {
        // Load shapes from database (or repository)
    }
    private double CalculateSurfaceArea(MyShape shape)
    {
        // Calculation of the surface area based on the dimensions and shape type
    }
}

Oder der Service arbeitet ohne Datenbank und bekommt stattdessen eine Liste mit Shape-Models, die die Grunddaten (Typ und Maße) enthalten.
Das Interface ist nicht zwingend notwendig, ist aber Gold wert, wenn Du die ViewModels automatisch testen willst und die nur das Interface kennen müssen - dann kannst Du das Interface mocken und z.B. Fehler provozieren und den Umgang damit testen lassen.

Kann man eine Service - Klasse auch grob als Methodenklasse mit statischen Methoden bezeichnen?

Wenn Du das "statisch" im übertragenen Sinn meinst, könnte man das so sehen, ja.
Häufig sind diese Methoden unabhängig voneinander, sie sind also nur "zufällig" in der selben Klasse.
Ob das nun der ideale Weg ist, sei mal dahin gestellt, aber die großen Ziele (Trennung von Verantwortlichkeiten und Testbarkeit) kann man damit erreichen und das ist das wichtigste.

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.

S
Spooner8 Themenstarter:in
40 Beiträge seit 2020
vor 2 Jahren

Das klingt, als würde das Model ein ViewModel als Parameter bekommen?

Nicht ganz. Das CalculationViewModel welches an die View gebunden wird hat ein ItemViewModel welches aus eigenen Eigenschaften, dem SubItemViewModel, dem DimensionsViewModel und dem AddonViewModel besteht. Das nur damit das ItemViewModel nicht 40 Eigenschaften haben muss. (Es ist leider ein sehr komplexes Item)
Wenn ich nun das ItemViewModel kopiere übergebe ich dem Command das ItemViewModel selbst im Konstruktor. Beim SubViewModel übergebe ich das ItemViewModel als Konstruktor weil das die Liste der SubViewModel beinhaltet und als CommandParameter dann das selektierte SubviewModel um es zu kopieren.
Wenn ein ViewModel einen Konstruktor hat der auf die Datanbank zugreift, dann eigentlich nur um Dropdowns zu befüllen.

Wenn Du das "statisch" im übertragenen Sinn meinst, könnte man das so sehen, ja.

Ich habe das eigentlich so gemeint, dass ich eine Klasse "CalculateAreasService" erstelle die z.B. so aussehen würde.


public static void CalculateAddonAreas(CalculationViewModel viewModel)
{
    foreach(ItemViewModel item in viewModel.ItemList)
    {
        item.AreaLeft = //irgendwas
        item.AreaRight = //auch irgendwas
    }
}

So hätte ich das als reiner Service genutzt und kann die Klasse ohne Instanz rein als "Mach irgendwas" Klasse verwenden. Darum meine Worte statisch und Methodenklasse

2.079 Beiträge seit 2012
vor 2 Jahren

Wenn ein ViewModel einen Konstruktor hat der auf die Datanbank zugreift, dann eigentlich nur um Dropdowns zu befüllen.

Konstruktoren sollten NIEMALS irgendwas mit der Datenbank tun! Eigentlich generell nichts, was lange dauern kann.
Konstruktoren bereiten die Grundfunktionalität vor, sind dabei in den Möglichkeiten aber sehr beschränkt.
Wenn Du Daten laden willst, dann baue eine Load- oder Reload-Funktionalität ein und rufe die dann aus der View (oder von irgendwo sonst) heraus auf.

Dem Rest konnte ich aber nicht ganz folgen.

Ich habe das eigentlich so gemeint, dass ich eine Klasse "CalculateAreasService" erstelle die z.B. so aussehen würde.

Dann siehe meine erste Antwort auf den Kommentar 😉
Nicht static!

Außerdem ist ein Business-Service eine Schicht unter der View, ViewModels gehören aber zur View.
Deine Beispiel-Methode ist also eine Verletzung der Schichten-Trennung.

Oder Du willst es nicht als Business-Service aufbauen, weil es zu "unwichtig" ist, um als eigener Business-Service aufgebaut zu werden.
Das wäre denkbar (mach ich auch manchmal), aber auch dann solltest Du keine direkten Abhängigkeiten (ViewModel als Parameter) verwenden, da Du dann neben dem ausgelagerten Code alle anderen Vorteile verlierst.

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.

S
Spooner8 Themenstarter:in
40 Beiträge seit 2020
vor 2 Jahren

Konstruktoren sollten NIEMALS irgendwas mit der Datenbank tun! Eigentlich generell nichts, was lange dauern kann.

Ich hab das mal kontrolliert. Ich habe aktuell in zwei ViewModels (SubViewModels von dem ItemViewModel) im Konstruktor einen Zugriff in die DB um Dropdowns zu füllen.
Das würde sich einfach beheben lassen wenn ich im Konstruktor nicht den Zugriff mache sondern die Mothode innerhalb von dem besagten ViewModel im Konstruktor aufrufen würde. Sofern das so gemeint war.
Bei zwei weiteren Klassen habe ich den Zugriff nicht im Konstruktor, sondern als Loaded Event einer View aufgerufen. Die Loaded Methode steht dabei im ViewModel und wird von der View dann nach dem Initialisieren angestossen. Das dürfte dann gut sein so richtig?
Das ist einmal ein UserControl_Loaded Event und einmal ein Window_Loaded Event.

Ich muss mich glaub mal entschuldigen, da ich das irgendwie nicht zusammen bekomme =(

Verstanden habe ich, das die View kein Codebehind haben darf ausser es ist irgendwie DragMove() oder was direkt die View selbst betrifft.
Das ViewModel ist dann die Datenkanone die alle Werte zur Verfügung stellt die im View angezeigt wird mit dem Binding. View kennt ViewModel aber nicht anders rum.
Auch ist klar, dass Buttons bzw. Befehle um per (wohl meistens) Klick irgendwas zu machen über einen Command laufen sollen. Ausgelöst per Binding von der View ans das ViewModel welches dann an den Command übergibt. Ein Command kann einen Konstruktor haben um teile des ViewModels zu kennen und zusätzlich auch Parameter über die View erhalten. Wenn auch indirekt, da der eigentliche Parameter ja dennoch aus dem ViewModel kommt.
Und ab dort bin ich irgendwie auf Eiern...
Eigentlich wollte ich später ein Model erstellen um dann eine XML zu erstellen. Die Daten holt sich das Model vom ViewModel (oder bekommt sie übergeben) um zu serialisieren. Viel mehr Sinn hatte ich bis dato in einem Model nicht gesehen, da es wie ich meinte nur Eigenschaften haben soll und keine Logik anhand von Methoden oder sowas wie INotifyPropertyChanged Sachen.
Oderarber ich habe das nicht realisiert und ein Kalkulations Service oder sonstige Tätigkeiten gelten auch als Model? Ich bin verwirrt sorry. Ich kann diese vielen Dinge nicht ganz zuordnen da ich dachte es gibt 3 Bereiche (Model, View, ViewModel) und dann kam Th69 und sagt was von weiteren Bereichen wobei er den Service zum ViewModel gepackt hat.
Zu trennen zwischen Business - Logik und Logik die das ViewModel machen soll ist mir nicht klar.

Falls mal jemand Zeit und Lust hat, ich bin gerne Bereit mal ein paar Euros für ne Stunde zu investieren um das in einem Gespräch 1:1 zu besprechen und einen kurzen Blick auf mein Programm zu werfen. Euer Wissen ist so enorm riesig und speziellen Dank an euch drei die hier aktiv Zeit investiert, dass Ihr mich als "Neuling" etwas überrennt damit.
Ich will es verstehen und richtig umsetzen, es ist aber etwas zu viel auf alles zu achten.

Danke nochmal.

F
10.010 Beiträge seit 2004
vor 2 Jahren

Es ist gar nicht so schwer.

Wenn du dir das 3 Schichten Model anschaust, siehst du das es neben
dem View noch die Logik- und die Datenschicht gibt.
MVVM als gesamtes ist lediglich die View Schicht. Es ist also nur das, was man für die Ansicht und Bearbeitung der visuellen Daten benötigt.

Die Logik ( z.b. durch Services ) und die Datenhaltung sind da nicht "drin" enthalten.

Wenn du das alles richtig trennst, kannst du die komplette Anwendung durch Unittests "simulieren"/Testen, ohne auch nur ein Fenster öffnen zu müssen.

Und Model ist wirklich nur ein Datenmodel.
Sobald du Berechnungen machst ( ausser mal ein x+y ) oder Daten holst, dann ist das kein Model, sondern ein Service.

2.079 Beiträge seit 2012
vor 2 Jahren

Das würde sich einfach beheben lassen wenn ich im Konstruktor nicht den Zugriff mache sondern die Mothode innerhalb von dem besagten ViewModel im Konstruktor aufrufen würde. Sofern das so gemeint war.

Och das hatten wir doch schon 🙁
Es interessiert nicht, ob der Code im Konstruktor steht, sondern ob er darin aufgerufen wird.
Genauso auch Probierties, die sollten möglichst nichts langwieriges machen.

Bei zwei weiteren Klassen habe ich den Zugriff nicht im Konstruktor, sondern als Loaded Event einer View aufgerufen. Die Loaded Methode steht dabei im ViewModel und wird von der View dann nach dem Initialisieren angestossen. Das dürfte dann gut sein so richtig?

Das klingt schon besser.
Ich würde den EventHandler aber nicht im ViewModel deklarieren, sondern in der View und darin rufst Du dann eine eigene Reload-Methode auf.
Noch besser wäre mMn. aber, wenn es ein Command ist und der aus dem XAML heraus aufgerufen wird.
Ob das notwendig ist ... kann man vermutlich drüber streiten, ich mache es aber immer so.

Ein Command kann einen Konstruktor haben um teile des ViewModels zu kennen und zusätzlich auch Parameter über die View erhalten. Wenn auch indirekt, da der eigentliche Parameter ja dennoch aus dem ViewModel kommt.

Ich habe es bisher nur selten gesehen, dass ein Command als eigene Klasse implementiert wurde, meistens nutzt man dafür RelayCommands.
Beides ist aber richtig, nur ist es bei einer eigenständigen Klasse umständlicher, das ViewModel zu steuern.

Die Daten holt sich das Model vom ViewModel (oder bekommt sie übergeben) um zu serialisieren.

Models sind dumm, die holen sich nichts und tun auch nichts.
Models transportieren Daten in einer Form, wie Du sie brauchst.
Das kann eine DB-Entity sein, oder ein DTO zwischen Service und ViewModel oder irgendetwas anderes, aber sie haben alle eines gemeinsam: Sie tun nichts.
Der Service bekommt ein Model (oder mehrere) übergeben oder liefert sie als Rückgabewert oder arbeitet intern damit - die eigentliche Logik befindet sich im Service.

Viel mehr Sinn hatte ich bis dato in einem Model nicht gesehen

Der Sinn ist das Transportieren von Daten in genau der Form, wie sie gebraucht werden.
Methode X braucht ganz bestimmte Eigenschaften aus der View, also übergibst Du NICHT das ViewModel, sondern erzeugst ein passendes Model mit den benötigten Eigenschaften und übergibst dieses Model.
Und Methode X gibt selber wieder ein Model zurück, mit Eigenschaften, die Du im ViewModel lesen und verwenden kannst.

ein Kalkulations Service oder sonstige Tätigkeiten gelten auch als Model

Nein.
Service => Logik
Model => Daten
DTO (Data Transfer Object) => Model => Daten

Ich kann diese vielen Dinge nicht ganz zuordnen da ich dachte es gibt 3 Bereiche (Model, View, ViewModel) und dann kam Th69 und sagt was von weiteren Bereichen wobei er den Service zum ViewModel gepackt hat.
Zu trennen zwischen Business - Logik und Logik die das ViewModel machen soll ist mir nicht klar.

So könnten die drei Schichten aussehen.

`- Presentation ¬

    • View (nutzt ViewModels) <-- Das erste "V" in MVVM
    • ViewModel (nutzt Services und übergibt/empfängt Models) <-- Das "VM" in MVVM
  • Business ¬
    • Models (Dumme Daten) <-- Das erste "M" in MVVM
    • Services (Empfängt oder liefert Models) <-- Da ist die Logik
  • Data ¬
    • Repository (liest/schreibt Daten aus der Datenquelle) <-- Da werden z.B. XMLs gelesen/geschrieben
    • Datenquelle (Datenbank, XML, etc.)`

Allerdings ist das Model-"M" in MVVM in der Darstellung vielleicht etwas missverständlich.
Das Model-"M" in MVVM meint alle Schichten unterhalb der View, also in diesem Fall "Business" und "Data".
Wenn man die Trennung aber strikt durch zieht und das ViewModel nur auf Business-Models zugreifen darf (nicht auf Data), befindet sich das Model effektiv nur an dieser einen Stelle - daher habe ich es dort auch eingezeichnet.

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.

4.939 Beiträge seit 2008
vor 2 Jahren

Ich kann diese vielen Dinge nicht ganz zuordnen da ich dachte es gibt 3 Bereiche (Model, View, ViewModel) und dann kam Th69 und sagt was von weiteren Bereichen wobei er den Service zum ViewModel gepackt hat.
Zu trennen zwischen Business - Logik und Logik die das ViewModel machen soll ist mir nicht klar.

Das meinte ich nicht so. Mit den eckigen Klammern in

View -> ViewModel [-> Services] -> Model (Logik)

habe ich ausdrücken wollen, daß die ViewModels optional auf Service-Klassen zugreifen oder eben direkt auf die (Business-)Logik.
Ich meinte hier ausdrücklich Services, die zur UI-Schicht gehören (nicht wie Palladin007 es verallgemeinert hat).

In großen Projekten sind diese 3 Hauptschichten oftmals in eigenen Assemblies untergebracht und kommunizieren mittels Schnittstellen (interfaces) und POCO/DTO-Klassen.
Und da muß man eben genau drauf achten, welchen Code man in welche Schicht packt (um die Abhängigkeiten auch möglichst gering zu halten).

S
Spooner8 Themenstarter:in
40 Beiträge seit 2020
vor 2 Jahren

Hallo zusammen, ich bins mal wieder

Ich bin doch einige Schritte weiter gekommen. DAnke noch für die letzten ausfürhungen, das hat mir geholfen und soweit ist Version 2 von meinem Programm nun am Start und tut was es soll inkl. dem Serialisieren und Deserialisieren was dieses Thema ursprünglich ausgelöst hat 🙂

Aktuell hängts aber mit dem Thema Dispatcher. Da das vorher alles direkt im Codebehinde geschrieben war, konnte ich mit


Dispatcher.Invoke(() => { CustomersList.Add(customer); });

eigentlich direkt einzelne Customer in die View laden.
Da ich das ja nun alles schön getrennt habe und ich mit Dispatcher nur Videos finde wo eben genau dieser Weg direkt im Codebehinde der View gezeigt wird, fehlt mir hier ein kleines Puzzelteil.

Ich habe einen Command welcher eine async Execute Methode verwendet. Dort hole ich kurz den Filename und mache dann das:


await Task.Run(() => new LoadCustomerImportFileService().LoadFile(filename, vm.Progress, vm.CustomersList));

Steinigt mich nicht falls dass bei euch wiedermal Augenkrebs auslöst 😁

In der Methode LoadFile hole ich dann aus einer Excel die Daten und erstelle jeweils ein Customers-Object (DTO)
Und dann wollte ich das hier machen:


Dispatcher.Invoke(() => { CustomersList.Add(customer); });

Soweit ich das beurteilen kann meckert er jetzt beim Dispatcher, weil das ViewModel wo er die Liste stück für stück befüllen soll, ja in einem anderen Thread läuft. Leuchtet mir natürlich ein. Aber ich habe noch nicht ganz verstanden wie ich nun von hier auf den anderen Thread zugreifen kann. mit "vm.Dispatcher.Invoke() ging es auch nicht als ich ihm das ViewModel mitgegeben habe.
Mir fehlt da der Zusammenhang mit der Funktionsweise. Hat jemand vielleicht ein Beispiel im MVVM Stil wie man eine Liste asynchron laden würde?

An sonsten bin ich bisher sehr zufrieden mit dem was ich geschafft habe. Ich kann mich nicht genug bedanken für all eure Geduld und Tips.
Kalkulationen für ein komplexes Produkt können nun eröffnet, konfiguriert, kalkuliert, gespeichert, geladen und geändert werden und das ganze noch in 4 Sprachen und Kundenbezug. Macht spass.

Merci aus der Schweiz 😉

2.079 Beiträge seit 2012
vor 2 Jahren

Steinigt mich nicht falls dass bei euch wiedermal Augenkrebs auslöst

Schade 😠

  1. Punkt:
    Dein Service lädt nicht nur Daten sondern fügt sie auch noch irgendwo hinzu.
    Das mag kleinkariert klingen, ist aber eine zweite Aufgabe und dir steht's gerade im Weg.

Also warum so kompliziert?
Warum gibt die LoadFile-Methode nicht einfach die geladenen Datensätze zurück?


var myData = await Task.Run(() => new LoadCustomerImportFileService().LoadFile(filename, vm.Progress));

vm.CustomersList.AddRange(myData);

Und ab dem Punkt sollte das await dein ganzes Problem übernehmen.
Wobei das "vm.CustomersList" so aussieht, als würdest Du hier VierModel-übergreifend arbeiten? Muss nicht schlecht sein, aber sieht ein bisschen danach aus.

  1. Punkt:
    Warum liest die LoadFile-Methode synchron? IO-APIs können meistens asynchrone Vorgänge, also warum nutzt Du die nicht?
    Als Problem fällt mir dabei aber allerdings nur ein, dass Du auf diese Weise einen ThreadPool-Thread unnötig lange blockierst, bei dir dürfte das aber kaum ein Problem sein.
    Außerdem ist ein vollständig asynchroner Ablauf nicht gerade einfach, spätestens wenn es dazu kommt, mehrere asynchrone Abläufe untereinander zu synchronisieren, wird's schwer.

Behalte es daher nur im Hinterkopf, dass Du - wenn Du soweit bist - lieber bereits implementierte asynchrone Methoden nutzt, wenn es sie auch gibt.
Bis dahin funktioniert deine Variante auch und ist - solange Du keinen anderen gravierenden Fehler gemacht hast - auch kein großes Problem.

  1. Punkt:
    Du erzeugst den Service selber - also das "new LoadCustomerImportFileService()" ist das Problem.
    Lösen kann man das mit DependencyInjection, allerdings glaube ich, dass es etwas zu weit geht, wenn Du dieses Riesen-Thema auch noch auf machst 😁
    Außerdem ist DI in WPF alles andere als einfach, da WPF ursprünglich nicht dafür gebaut wurde, man es also mehr schlecht als recht nachrüsten muss.
    Es gibt zwar Frameworks, die das versprechen, aber die arbeiten (soweit ich das überblicken kann) immer mit dem ServiceLocator-Pattern, aber das mag ich gar nicht.

Also betrachte diesen Punkt auch erst im Hinterkopf, kann gut sein, dass das Thema weit über's Ziel hinaus schießt.

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.

S
Spooner8 Themenstarter:in
40 Beiträge seit 2020
vor 2 Jahren

Zu Punkt 1:
Jop, hast du recht. Kann ich ja relativ einfach trennen.

Also warum so kompliziert?
Warum gibt die LoadFile-Methode nicht einfach die geladenen Datensätze zurück?

Öhm das ist ein Argument was ich gerne prüfen werde. Ich hatte diesen Aufbau aus einem Video und hat mir soweit eingeleuchtet, da ich ja so in einem anderen Thread arbeite damit das Window nicht einfriert. Wie gesagt da weiss ich noch nicht wer, was, wann, wo kennt, trennt und macht damit alles rund läuft. Da bin ich mich gerade am einfinden.

Wobei das "vm.CustomersList" so aussieht, als würdest Du hier VierModel-übergreifend arbeiten? Muss nicht schlecht sein, aber sieht ein bisschen danach aus.

Ich übergebe mit dem Command das ViewModel, damit ich das alt bekannte Dilemma nicht mehr habe einen Converter zu brauchen nur um 3-4 Parameter übergeben zu können die im ViewModel stecken. Darum ist der Commandparameter schlicht {Binding Mode=OneWay}

Zu Punkt 2:
Der Command führt sein Execute ja async aus. Der wiederum braucht ja dann einen Prozess auf den er warten kann mit dem "await". Wenn ich nun die LoadFile Methode auch async mache worin das Laden erfolgt welches den Freez auslöst, braucht der ja auch wieder auf etwas auf das er warten kann, aber es kommt dahinter nichts mehr.

Zu Punkt 3:
Ich heul gleich hahaha. Hab ich jetzt mal registriert und werde es auf meine To Do List für 2023 schreiben 😜
Das ist übrigens das was ich früher mal meinte mit dem Service. Damit ich das nicht instanziieren muss dachte ich kann ich die Methode darin eben "static" machen, damit sie einfach aufgerufen werden könnte. Da hast du mir gesagt "Nein". Hatte mich da schon gefragt ob es dann wirklich immer mit "new" gemacht werden soll, weil ich das selbst jetzt nicht so wirklich sexy gefunden habe. Aber wie gesagt, das Thema "DependencyInjection" reisse ich jetzt nicht an. Aber zumindest weiss ich mal ein Schlagwort wie man es in diesem Fall machen würde.

S
Spooner8 Themenstarter:in
40 Beiträge seit 2020
vor 2 Jahren

Ok jetzt hat es funktioniert wenn ich die Liste zurück gebe und dann die ObservableCollection damit befülle.

Pro: Es läuft und das Ergebnis ist richtig
Contra: Beim Laden füllt es nicht die Liste laufend, sondern am Ende auf einen Schlag.
Pro 2: Die ProgressBar funktioniert zum Glück und da wird der Status dann angezeigt, damit der User weiss das etwas läuft.

Werde es vermutlich so lassen, da auch das Thema Freez wie gewünscht erledigt ist.

2.079 Beiträge seit 2012
vor 2 Jahren

Öhm das ist ein Argument was ich gerne prüfen werde. Ich hatte diesen Aufbau aus einem Video und hat mir soweit eingeleuchtet, da ich ja so in einem anderen Thread arbeite damit das Window nicht einfriert.

Ist ja auch richtig.
Beim async/await hast Du dann aber nicht einen Thread, sondern prinzipiell viele, immer dann, wenn das Programm auf irgendwas (z.B. IO) warten muss.

Du kannst natürlich auch eine Liste übergeben und die live füllen, aber dann hast Du wieder das Problem, was Du anfangs hattest.
Das zu umgehen ist mMn. aber etwas schwieriger, außerdem bin ich ein großer Fan von Methoden nach dem einfachen EVA-Prinzip: Eingabe, Verarbeitung, Ausgabe.
Bei einer Liste, die Du als Parameter übergibst und dann füllst, ist dieses Prinzip nicht so ganz deutlich.
Prinzipiell falsch ist es aber nicht, nur eben anders und sicher auch vom Programmier-Stil abhängig.

Ich übergebe mit dem Command das ViewModel, damit ich das alt bekannte Dilemma nicht mehr habe einen Converter zu brauchen nur um 3-4 Parameter übergeben zu können die im ViewModel stecken. Darum ist der Commandparameter schlicht {Binding Mode=OneWay}

Hm ... ich kenne deinen Aufbau nicht, aber mit dem Aufbau kettest Du die ViewModels aneinander. Ist jetzt eigentlich keine MVVM- oder Schichten-Verletzung, aber vielleicht geht's auch besser.
Z.B. könntest Du ein Interface verwenden. Oder Du hast den Command im "übergeordneten" ViewModel, was die Items sowieso kennt, oder ...
Es gibt viele Wege, dein Weg ist nicht automatisch falsch, nur anders, als ich es machen würde.

Der Command führt sein Execute ja async aus. Der wiederum braucht ja dann einen Prozess auf den er warten kann mit dem "await". Wenn ich nun die LoadFile Methode auch async mache worin das Laden erfolgt welches den Freez auslöst, braucht der ja auch wieder auf etwas auf das er warten kann, aber es kommt dahinter nichts mehr.

Ich gehe davon aus, dass Du eine Datei liest?
Dann hast Du da das Warten: Das Lesen der Datei dauert.
Und IO-Operationen (also auch Dateien lesen) haben meistens bereits eine Async-Implementierung, die Du verwenden kannst.

Ich heul gleich hahaha.

Ich hoffe, Du dachtest nicht, dass Du fertig bist? 😉
Das Thema hört nie auf und DependencyInjection ist nur - meiner Meinung nach - der nächste logische Schritt nach der Schichtentrennung.

Haupt-Ziel: Trennung von Abhängigkeiten.
Die Verantwortlichkeiten hast Du getrennt, aber immer noch sind die Klassen voneinander abhängig, was die ganze Software mit der Zeit sehr starr macht, was Du nicht willst. DependencyInjection ist ein Weg, damit umzugehen.

Statische Methoden in anderen Klassen sind dabei keine Hilfe, da die Klassen ja immer noch voneinander abhängig sind - mit static sogar noch ein gutes Stück mehr.

Hatte mich da schon gefragt ob es dann wirklich immer mit "new" gemacht werden soll

Kommt drauf an, manchmal ja, meistens nein.
Das Problem mit "new" ist, dass Du an der Stelle, wo Du die Instanz erzeugen willst, dann zwangsläufig auch alle Abhängigkeiten brauchst.
In deinem Fall ist das egal, aber sobald Du z.B. Einstellungen weit in die Details verteilen willst, oder noch schlimmer, nachträglich Abhängigkeiten ergänzen willst, stellst Du fest, dass sich dabei schnell eine elendig anstrengende Kette aufbaut, bei der dann am Ende irgendwie alles von allem abhängig ist.

Das kann man natürlich auch anders umgehen, DependencyInjection ist ein Weg, der viel Verbreitung gewonnen hat.
Ein anderer Weg wäre das ServiceLocator-Pattern, aber davon rate ich ab ^^

Der "Nachteil" (für einen Anfänger) von DependencyInjection ist, dass man den recht einfachen straight forward Ablauf aufgeben muss, Du musst also umdenken.
Jeder Service ist prinzipiell für sich alleine gestellt und unabhängig, (im besten Fall) alle Abhängigkeiten kommen per Konstruktor rein und genauso bekommen andere Klassen auch eine Instanz auf die Services.
Diese andere Art, ein Problem anzugehen, musst Du erst lernen und das fällt manchen nicht leicht.
In deinem Fall wäre das vermutlich ein mittelgroßer Umbau (auch, weil das mit WPF schwierig ist), deshalb solltest du dich auch erst daran versuchen, wenn die Basics, die dahin führen, wirklich in Fleisch und Blut übergegangen sind. Ohne wirklich verinnerlicht zu haben, wie man Aufgaben in Services trennt, kannst Du die Services nicht unabhängig voneinander "dependencyinjecten" lassen 😉

Contra: Beim Laden füllt es nicht die Liste laufend, sondern am Ende auf einen Schlag.

Wenn das Lesen der Datei asynchron und klug aufgebaut ist, kannst Du die IAsyncEnumerable anschauen.
Das funktioniert aber nur, wenn Du keinen Task drum herum startest, sondern die Methode in sich asynchron arbeiten kann, außerdem bringt das wieder ein paar Schwierigkeiten mit.
Und es gibt noch viele andere Wege, das IAsyncEnumerable wäre nur mein persönlicher Weg.

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.

S
Spooner8 Themenstarter:in
40 Beiträge seit 2020
vor 2 Jahren

Du kannst natürlich auch eine Liste übergeben und die live füllen, aber dann hast Du wieder das Problem, was Du anfangs hattest.

Das wäre das Ziel gewesen, weil ich es persönlich schöner finde wenn eine Liste die geladen werden muss live geladen wird. Mit der ProgressBar ist das aber auch in ok und werde ich so lassen. Abgesehen davon, dass es ja so auch keine Probleme mehr gibt. Irgendwann werde ich mich mit async / tasks und threads noch aktiver auseinander setzten um zu verstehen wie das genau funktioniert. das es mit einer async Methode an sich schon funktioniert wusste ich nicht, bin aber froh drum. Das macht es mir an anderen Stellen dann auch einfacher später.

Hm ... ich kenne deinen Aufbau nicht, aber mit dem Aufbau kettest Du die ViewModels aneinander. Ist jetzt eigentlich keine MVVM- oder Schichten-Verletzung, aber vielleicht geht's auch besser.

Jaaa das kann man so sagen, allerdings nur bedingt. Ich mache nicht bzw. wenn dann nur ganz kurze "Ketten" um etwas durch zu reichen. Das ist dann jeweils ein Objekt was auch innerhalb der Methode / Service oder was auch immer entsprechend direkt geändert werden kann ohne die Daten durch alles zurück schicken zu müssen.
Im grossen und ganzen ist es aber eher Sternförmig. Der Auslösepunkt übergibt (meist per Command) das ViewModel im aktuellen Zustand weiter. So kann ich gewährleisten, dass eben nich zu grobe Abhängigkeiten vorhanden sind wo ich dann plötzlich nicht mehr wüsste woher ein Fehler kommen könnte.
Durch den neuen Aufbau sind die Klassen auch echt klein geworden und das hilft enorm bei der Arbeit. =) Danke für den Ansporn dazu.
Ein Inferface macht bei mir halt keinen wirklichen Sinn, da es nur 1 Itemsklasse und 1 Subitemsklasse gibt die immer auf die selbe Art und weise interagieren müssen. Flexibilität dieser beiden Klassen ist = 0. Spätere Anpassungen werden leider unvermeidbar dazu führen den bestehenden Code an zu fassen. Nicht cool, aber nun mal dem Produkt geschuldet.

Ich gehe davon aus, dass Du eine Datei liest?

Ja es sind jeweils Excel Dateien. Einmal die Kundenliste die ab und an erneuert wird und für dieses Programm dann eingelesen und anschliessend in SQL-Datenbank übergeben wird. (Einlesen, damit man vor dem definitiven übergeben an die DB noch Einträge korrigieren könnte) Das sind knapp 6000 Einträge und einmal die verschiedenen Preise für Produkteteile. Das sind eher kleine Listen im Vergleich, soll aber zukunftsorientiert bereits jetzt asynchron geladen werden um nie einen Freez zu riskieren.

Ich hoffe, Du dachtest nicht, dass Du fertig bist?

Nene, fertig gibts nicht. Das ist mir schon klar und soll auch so sein. Ich will nur nicht für jedes Problem was in dem ersten Projekt auftaucht immer die absolut perfekte Richtung einschlagen nur weils das gibt. Das ganze soll schon sehr gut werden, aber man kann nicht alles auf einmal lernen. Gewisses muss eben erst richtig sitzen wie du schon sagst. Ich habe die Basics mehr oder weniger drauf, wobei Basics nicht bei jedem gleich viel bedeutet. Async sehe ich jetzt nicht ganz bei den C# Basics.

Du hast mir ja mal Stichworte gegeben wie ich das mit dem "new" vermeiden könnte. einmal mit DependencyInjection, einmal mit IAsyncEnumerable oder auch mit dem ServiceLocator-Pattern. Sagt mir stand heute alles überhaupt nichts, aber es wird der Tag kommen wo das zum Thema wird. Nicht in diesem Projekt haha.

PS: Wenn es dich interessieren würde mal über meinen Code zu kucken, können wir sicherlich mit einem Stream oder so was richten. Den Code verschicken eher nicht. 1. weil ich das Inoffiziell für die Firma privat mache und unter Verschluss passiert und 2. weil die Datenbank ja noch dran hängt. Obwohl ich da nicht weiss ob es einfache Wege dafür gäbe. --> Ich wollte auf dem Laptop mal von extern programmieren, aber das mit der Datenbank hat mich blockiert.

2.079 Beiträge seit 2012
vor 2 Jahren

Ja es sind jeweils Excel Dateien.

Excel kann nervig sein zum Lesen 😠
Für XLSX empfehle ich "ClosedXml", das ist mMn. sehr intuitiv und hat mir schon ein paar Mal viel Kopfzerbrechen erspart.
Leider kann es kein Async (oder ich weiß nicht wie), dann wäre es in Ordnung, wenn Du das in einem eigenen Task kapselst, allerdings dürfte das IAsyncEnumerable (sofern Du es nachrüstest) etwas schwieriger werden. Bestimmt findet sich da eine geeignete async-fähige Queue, mit der man das richtige Verhalten erreichen kann.

Du hast mir ja mal Stichworte gegeben wie ich das mit dem "new" vermeiden könnte. einmal mit DependencyInjection, einmal mit IAsyncEnumerable oder auch mit dem ServiceLocator-Pattern. Sagt mir stand heute alles überhaupt nichts, aber es wird der Tag kommen wo das zum Thema wird. Nicht in diesem Projekt haha.

Vorsicht: Nicht vermischen!
DependencyInjection und ServiceLocator sind Ansätze um Abhängigkeiten in der Anwendung zu verteilen bzw. anders herum, wie eine Klasse an ihre Abhängigkeiten kommt. Das "new" ersetzt es nicht, es macht es nur weitläufig obsolet.
Und IAsyncEnumerable hat damit genau nichts zu tun 😉

Async sehe ich jetzt nicht ganz bei den C# Basics.

Wenn Du mit WPF, ASP.NET, etc. arbeitest, wird das aber schnell notwendig 😉

Was aber definitiv zu den Basics gehört, weil es wirklich überall drin steckt: IEnumerable.
Ich habe schon viele kennen gelernt, die nicht wirklich begriffen haben, was das ist und welche Möglichkeiten, aber auch Gefahren sich dahinter verbergen. Stichwort: Iterator-Prinzip
Das IAsyncEnumerable ist im Grunde das gleiche, nur eben asynchron.

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.

S
Spooner8 Themenstarter:in
40 Beiträge seit 2020
vor 2 Jahren

Ja Excel kann schon nerven. Leider bekomme ich diese Listen nur in Excel. Ich habs jetzt mit Microsoft.Office.Interop.Excel gemacht.

Vorsicht: Nicht vermischen!

Nene das schon nicht. Ich weiss ja noch nicht was es ist. Bezog sich nur auf deine Stichworte, damit ich Anhaltspunkte hab was es noch so für Optionen gibt.

IEnumerable muss ich nochmal anschauen. Habe ich auch noch zu wenig auf dem Schirm.

2.079 Beiträge seit 2012
vor 2 Jahren

Ich habs jetzt mit Microsoft.Office.Interop.Excel gemacht.

Mein Beileid.
Lass doch als XLSX speichern und lies das, das erspart dir grob über den Daumen gepeilt 90% der Arbeit 😁

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.

F
10.010 Beiträge seit 2004
vor 2 Jahren

Nicht nur das, dadurch ist es so schnell, das man auch kein IAsyncEnumerable mehr braucht.
Für "alte" XLS Files ist auch GitHub - nissl-lab/npoi: a .NET library that can read/write Office formats without Microsoft Office installed. No COM+, no interop. geeignet

S
Spooner8 Themenstarter:in
40 Beiträge seit 2020
vor 2 Jahren

Verdammt jetzt will ichs doch wissen haha. Baue ich das eben nochmal neu, ist ja nicht viel.
Da FZelle schon anspricht das es auch deutlich schneller funktioniert ist das definitiv das was ich auch will. Ist ClosedXML in beide Richtungen gut und vor allem Lizenzfrei?
Meine Variante wäre glaub Lizenzpflichtig um Excel Files zu generieren. Ich muss eben die Liste die Importiert wird, auch exportieren können. z.B. für Massenüberarbeitungen.

Als XLSX zu speichern ist kein Problem, daher wäre das schon ein guter Gedanke, das zu ändern. Vor allem weil ich das noch mehrfach bauen werde für andere Bereiche.

F
10.010 Beiträge seit 2004
vor 2 Jahren

Warum überhaupt Excel?

Fast alles kann CSV Dateien erzeugen/lesen, die lassen sich einfach verarbeiten und z.b. auch in Excel normal einlesen.

2.079 Beiträge seit 2012
vor 2 Jahren

Ist ClosedXML in beide Richtungen gut und vor allem Lizenzfrei?

Schau doch nach?

Warum überhaupt Excel?

Fast alles kann CSV Dateien erzeugen/lesen, die lassen sich einfach verarbeiten und z.b. auch in Excel normal einlesen.

Häufig ist aber explizit Excel gefordert, z.B. um Formate festlegen oder Styles anpassen zu können. Oder einfach nur, damit die Datei "xlsx" heißt.
Zumindest scheint das hier kein privates Projekt zu sein, sondern etwas, wo es auch einen Abnehmer für gibt?

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.