[WpfNote2] Mehrschichtanwendungen mit MVVM und Generischem Repository mittels EF Core

    • XAML: WPF
    • .NET (FX) 4.5–4.8

    Es gibt 35 Antworten in diesem Thema. Der letzte Beitrag () ist von MichaHo.

      [WpfNote2] Mehrschichtanwendungen mit MVVM und Generischem Repository mittels EF Core

      Hallo Leute

      Inspiration für dieses Beispiel waren folgende Threads:

      Unit of Work und Repository - GetAll Methode mit generischer Klasse und Filter
      MVVM OpenSource Communityprojekt - HomeStorage
      Raise Events nach oben durchreichen oder add Überladung funktioniert nicht

      Vorweg muss ich ein paar Dinge dazu sagen:
      1.) Dieses Beispiel ist mit Kanonen auf Spatzen geschossen. Niemand würde ein so kleines "Projekt" wie eine Notizapp mit diesem Schema aufziehen, eine Mehrschichtige Anwendung würde nur bei größeren komplexeren Projekten sinn machen. Je größer das Projekt desto mehr kommen die Vorteile von MVVM, einem generischem Repository und/oder einer Layertrennung zu tragen.
      Was Anfangs viel zu komplex erscheint gewinnt mit wachsender Solutiongröße an "einfachheit" da alles an seinem Ort ist und da gewisse abhängigkeiten Abstrahiert werden sind änderungen an einem der unteren Layer umso einfacher.

      2.) Sicher ist der Code und/oder die Struktur nicht das maß aller Dinge und jeder muss für sich und das Projekt bzw. die Aufgabenstellung selbst herausfinden wie es am besten zu machen ist und wie man seine Anwendung nun aufbaut. Ich setze diese Struktur allerdings bei einigen Projekten ein wo mir diese Struktur bereits mehrfach zu gute gekommen ist.

      Was ist dieses Beispiel und was nicht:

      Die Applikation soll eine mehrplatzfähige Notizverwaltung darstellen bei welcher Notizen angelegt, editiert oder wieder gelöscht werden können.
      Es können einer Notiz Anhänge hinzugefügt werden. Es gibt eine Protokollfunktion um Änderungen an Notizen, Anhängen und Kategorien nachvollziehen zu können.

      Es soll keine Eierlegende Wollmilchsau darstellen welche alles mögliche kann. Ich wollte mit dieser Testanwendung einfach nur zeigen was ICH wie machen würde um ans Ziel zu kommen ohne gewisse Pattern zu verletzen. Außerdem sollte die Anwendung durch den Einsatz und der einhaltung des MVVM Patterns zeigen wie eine Anwendung Testbar gemacht werden kann.
      Eines der Vorteile von MVVM ist ja eben das eine Anwendung durch z.b. UnitTests testbar wird. Wir können also jederzeit sehen ob noch alles an unserer Anwendung korrekt funktioniert - vorausgesetzt der Teil der Anwendung wurde durch einen oder mehrere UnitTests abgedeckt.

      Dennoch wollte ich auf einige schöne Features der WPF eingehen und zeigen wie soetwas realisierbar ist:
      Die Settings sind solch ein Beispiel. Im Model (Datenbank) ist ein Settingswert IMMER als String gespeichert. Dennoch sind in diesem Beispiel bei jedem "Setting-Knoten" andere Controls zu sehen um die bedienung für den Enduser so einfach und intuitiv wie möglich zu halten.


      Layer (Schichten):
      Jeder der sich MVVM schon mal angesehen hat weis das eine Anwendung unter MVVM schon mal aus mindestens 3 Schichten (oder auch 3 Projekte innerhalb der Projektmappe) besteht.
      Dem Model, dem ViewModel und der View.
      Hat man ein generisches Repository kommt schon mal mindestens eine Schicht hinzu.

      Ich gehe in diesem Beispiel einen Schritt weiter:


      In dieser Abbildung sieht man die Layer recht schön. Es gibt ein Model. Dieses beinhaltet unsere Entitäten. Diese sind reine POCO Klassen welche nichts anderes machen als Properties zur verfügung zu stellen. EntityFramework übernimmt diese "Struktur" aus der Objektorientierten Welt um daraus die relationelle Datenbank erstellen zu können.
      Dann gibt es den NotesContext welcher unser DBContext ist mit welchen wir Daten abrufen oder in die DB zurück persistieren können. Dieser besitzt einen Verweis auf Entity Framework Core.
      Als nächstes sehen wir unser Repository. Dieses ist ein generisches Repository welches auch einen Verweis auf das EntityFRamework besitzt, und stellt Methoden zum abrufen oder speichern von Daten über der DBContext bereit, allerdings OHNE den DBContext und somit EntityFramework nach aussen zu reichen. Womit wir zur nächsten Schicht kommen.
      Der Businesslogik. Diese hat nun keinen Verweis mehr auf das EntityFramework da unser Repository den Context vor der Businesslogik "versteckt". Die Businesslogik kümmert sich um die ganze Programmlogik. In dem Fall dieser einfachen Applikation ist es nicht viel was die Businesslogik macht, bei größeren Applikationen ist es allerdings so da man evtl. sehr viel an Logik implementieren muss. Beispiel: Ein Artikel wird verkauft. Es muss eine Lagerbewegung Protokolliert werden, Rabatte müssen berechnet werden, evtl. muss nachgesehen werden ob der mindestlagerbestand unterschritten wurde und die Logik muss beim Lieferanten automatisch nachbestellen. Dies würde alles die Businesslogik erledigen. So bleibt unser nächster Layer - das ViewModel - sauber von solchen Dingen.
      Tja, das ViewModel, ein ViewModel stellt im Grunde das dar was im View angezeigt werden soll. Unabhängig von der Struktur des Models oder anderer Abhängigkeiten.
      Oft ist es so das ich Daten völlig anders in einer Datenbank speichern möchte als ich diese anzeige. Ich möchte oder kann mein Model vieleicht nicht ändern (evtl. ist dieses sogar von EF generiert worden und ich kann gar nicht eingreifen). Das ViewModel bereitet die Daten welche die View anzeigen soll auf.
      Beispiel: In meinem Model habe ich eine Klasse (Klasse = ist in unserem Fall eine Tabelle in der Datenbank) Settings. Ein Setting ist immer als String gespeichert.
      Schön und gut, jetzt möchte ich aber an einer Stelle eine Listbox anzeigen welche mit Werte darstellt welche in den Settings als Kommata getrennter String stehen.
      Unser ViewModel wird hierfür eine List(Of String) bereitstellen welche die "Item" beinhaltet indem es den Kommatagetrennten String umwandelt. Umgekehrt aber beim Speichern wieder zurück in einen Kommatagetrennten String. So sind wir flexibel was die anzeige im View betrifft.
      Zu guter letzt die View. Die View hat keinen Verweis auf auf eine Businesslogik, ein Repository, einen DBContext oder gar EntityFramework. Auch ein Verweis auf das Model ist nicht vorhanden weil wir ja wie gerade erwähnt nicht an ein Model und deren Struktur gebunden sein sollen. Ändern wir etwas am Model haben wir später vieleicht Bindingfehler welche zur Kompiliertzeit nicht auffallen. Im ViewModel allerdings schon.

      UnitTests:
      Ich habe in die UnitTests jetzt nicht soo viel Arbeit investiert wie bei einem "echten" Projekt. Dennoch wollte ich aufzeigen das die komplette Applikation testbar ist.
      Wir wissen also immer innerhalb Sekunden ob wir durch unsere letzte Änderung etwas defekt gemacht haben, vorausgesetzt wird haben die betreffende Stelle mit einem Test abgedeckt.
      In diesem Projekt gibt es 40 UnitTests welche innerhalb von 4,19 Sekunden durchlaufen. Diese UnitTests können IMMER ausgeführt werden. Egal ob auch dem lokalen Rechner, auf einem Buildserver wie AzureDevOps oder am Mond. Es wird kein Internet, keine Datenbank oder sonst etwas benötigt. Würden die UnitTests eine Datenbankn benötigen würde diese VIEL länger brauchen da ja für jeden Test die DB neu erstellt und mit Beispieldaten befüllt werden müsste.



      Hier ein Beispieltest:

      VB.NET-Quellcode

      1. <TestMethod> <TestCategory("ViewModel.CategoryVm")> Public Sub SaveNewCategoryMustCreateProtocolTest()
      2. Using ctx As New NotesDbContext
      3. Using sut As New CategoryVm(New CategoryBl(New GenericRepository(Of NotesCategory)(ctx)), New NotesCategory)
      4. sut.Title = "NewCatTest"
      5. sut.Desc = "This is a Test"
      6. sut.DeletedFlag = True
      7. Task.Run(Sub() sut.Save.Execute(Nothing)).GetAwaiter.GetResult()
      8. Assert.AreEqual("NewCatTest", ctx.Categories.First.Title)
      9. Assert.AreEqual(1, ctx.Protocols.Where(Function(p) p.LinkedToCategoryId = sut.Id).Count)
      10. End Using
      11. End Using
      12. End Sub


      Dieser Test testet ob beim anlegen einer neuen Notizkategorie ein Protokoll in die Protokolltabelle für diese Kategorie geschrieben wird.


      OK, genug der vielen Wort - ich möchte auch nicht zu sehr ins Detail gehen. Wenn jemand Fragen hat soll er am besten einfach Fragen.
      Ich werde dann so gut wie ich nur kann versuchen die Frage zu beantworten. Gerne gehe ich hier dann auch auf gewisse Dinge detailierter ein.

      Das Projekt ist außerdem hier gehostet.

      So, jetzt noch Bilder der App:
      Dateien
      If _work = worktype.hard Then Me.Drink(Coffee)
      Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

      ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Nofear23m“ ()

      Ich habe mir das Projekt runtergeladen und wollte es mir ansehen.

      Starten kann ich es nicht, und die UserControls kann ich mir auch nicht ansehen.

      Fehler siehe Screenshots.
      Muss man da noch was anderes Installieren?
      Bilder
      • 1.png

        13,63 kB, 787×179, 286 mal angesehen
      • 2.PNG

        34,49 kB, 1.570×230, 310 mal angesehen
      Rechtschreibfehler betonen den künstlerischen Charakter des Autors.
      Du musst mal neu kompilieren bzw. die Nuget-Pakete wiederherstellen. Diese liefere ich natürlich nicht mit.
      Normalerweise sollte das aber VS von selbst beim kompilieren machen. [STRG] + [Shift] + B

      Grüße
      Sascha

      EDIT: Habs gerade hier am "unvorbelasteten" Firmenrechner versucht. Klappt. Gib bitte bescheid wenn du noch probleme haben solltest.
      If _work = worktype.hard Then Me.Drink(Coffee)
      Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

      ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

      exc-jdbi schrieb:

      dass es die Zusammenhänge zeigt

      Ja, mir ging es eben darum wie (gerade im zusammenhang mit EF) man über mehrere Layer Dinge erledigen kann. Gerade z.b. der changeTracker von EF wird vom Repository über die Businesslogik zum ViewModel gereicht.
      Und trotzdem funktioniert das Tracking wie gewohnt - ohne das ich aber EF nach "außen" reiche.

      Grüße
      Sascha
      If _work = worktype.hard Then Me.Drink(Coffee)
      Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

      ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

      @Akanel konntest du dein problem lösen??

      Grüße
      Sascha
      If _work = worktype.hard Then Me.Drink(Coffee)
      Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

      ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

      Hey,
      Ja das funktioniert nun. Sorry das ich es hier nicht gemeldet habe.

      Ich habe aber derzeit (wieder) ganz andere Probleme vom Verständnis her.
      Dazu mache ich aber einen eigenen Beitrag auf.
      Rechtschreibfehler betonen den künstlerischen Charakter des Autors.
      Hallo

      Nene, kein Problem - dachte nur das du vieleicht noch Hilfe benötigst um es zum laufen zu bringen.

      Grüße
      Sascha
      If _work = worktype.hard Then Me.Drink(Coffee)
      Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

      ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

      Hallo Sascha

      Ich hätte noch eine allgemeine Frage. Du schreibst:

      Nofear23m schrieb:

      vom Repository über die Businesslogik zum ViewModel gereicht.


      Das habe ich auch wahrgenommen, und versteh ich auch. Könntest du aber trotzdem z.B. in einem Satz beschreiben, was allgemein die BusinessLogic für eine Aufgabe hat. Ab wann macht es wirklich Sinn noch eine BusinessLogic-Schicht einzuführen.

      Danke für die Antwort.

      Freundliche Grüsse

      exc-jdbi

      exc-jdbi schrieb:

      Könntest du aber trotzdem z.B. in einem Satz beschreiben, was allgemein die BusinessLogic für eine Aufgabe hat. Ab wann macht es wirklich Sinn noch eine BusinessLogic-Schicht einzuführen.


      Ich denke das kann und sollte man nicht verallgemeinern. Das muss man von Anwendung zu Anwendung entscheiden.
      In so gut wie fast jeder Businessanwendung mit deren typischen Scenarien macht es sinn. Wie im eingangspost beschrieben.

      Ein einfaches Beispiel:
      Ein Benutzer ändert erfolgreich sein Kennwort. Was macht hier die Businesslogik in diesem Moment.
      Es püft das neue Kennwort auf komplexität und prüft ob es zulässig ist (z.b. dürfen die letzten 10 Kennwörter nicht verwendet werden, mindestens 6 Zeichen, mindesten ein Buchstabe und mindestens ein Sonderzeichen).
      Speichert das neue Kennwort, setzt in der DB dann auch noch das neue Ablaufdatum. Trägt das alte Kennwort in der Kennwort-History ein (wegen der "letzten 10 - Regel"), setzt des Timestämp-Flag in der DB und schreibt auch noch einen Protokolleintrag.

      Man sieht also. Nur für eine Passwortänderung muss eigendlich recht viel gemacht werden. Habe ich dies alles in meinen VM finde ich das unschön. Ich "verunreinige" mir an dieser Stelle das ViewModel. Das geht zu lasten der übersichtlichkeit (ist aber auch Geschmacksachen). Denn - habe ich eine Businesslogik weis ich genau wo der Code für solch eine Logik ist. Und zwar in der LoginBl bzw. in der UserBl, je nachdem wo ich es hinpacke.

      Ich hoffe das ich es einigermaßen verständlich erklären konnte.

      Schöne Grüße
      Sascha
      If _work = worktype.hard Then Me.Drink(Coffee)
      Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

      ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

      Also darf man sage: Prinzipiell aus Übersichtlichkeit, aus Servicegründen, aus Testgründen und schlussendlich zur Entlastung vom VM.

      Ich finde das ist nicht Geschmacksache. Und so wie du es gelöst hast, werde ich es in Zukunft auch machen.

      Danke für die Antwort.

      exc-jdbi

      exc-jdbi schrieb:

      Und so wie du es gelöst hast, werde ich es in Zukunft auch machen.

      Das freut mich natürlich.

      Ja, unter anderem mache ich es in größeren Projekten auch weil ich die BL dann überall verwenden kann. Wenn ich z.b. Teile der App in eine Web-Applikation bringen möchte, oder in einem Webservice verwenden will kann ich die BL und das Repository Referenzieren und muss mich um die ganze Logik nicht mehr kümmern. Ich habe die Methode "ChangePassword" und alles gut.

      Grüße
      Sascha
      If _work = worktype.hard Then Me.Drink(Coffee)
      Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

      ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

      Hallo,

      da du geschrieben hast, dass du auf bestimmte Dinge gerne detaillierter eingehst, frage ich hier einfach mal :D

      Nofear23m schrieb:

      Es gibt eine Protokollfunktion um Änderungen an Notizen, Anhängen und Kategorien nachvollziehen zu können.

      Das finde ich sehr spannend, nur sieht das recht kompliziert aus.
      Zu Beispiel gibt es ein Interface IProtocolable, dann gibt es eine BuissnesLogic.....
      Ich verstehe jetzt nicht ganz wie diese zusammen spielen, bzw. was ich in einer einfachen Anwendung (die noch nicht mal umbedingt nach dem MVVM Muster aufgebaut ist) alles brauche.

      Viele Grüße
      Forian
      ----

      WebApps mit C#: Blazor
      Hallo Florian

      Jaja, die Protokollfunktion. Die ist schon cool wie ich persönlich finde. Mir ist es immer wichtig das ich in einer Anwendung - vorallem dann wenn mehrere Personen damit arbeiten - eine Protokollfunktion habe mit welcher ich wenn möglich so flexibel wie möglich änderungen protokollieren kann. Dafür habe ich mir ein paar Dinge überlegt.

      Fangen wir beim Model an. Da ich gerne mit Interfaces Arbeite habe ich eben das IProtocollable-Interface. Jede Modelklasse welches dieses Interface implementiert hat automatisch die Funktionalität ein Protokoll zu schreiben da das Interface eine Eigenschaft Protocol As ICollection(Of Protocol) besitzt. Warum als Interface und nicht einfach als Eigenschaft. Ich kann so später im Code abfragen ob ein Objekt dieses Interface implementiert und dann darauf reagieren.

      Weiter... Sobald diese Eigenschaft vorehanden ist legt uns EF Core auch automatisch die dazu passende ID-Spalte in der Tabelle Protocol an. Also wenn die Entität Note nun das Interface implementiert wird eine NoteID Spalte angelegt.

      In der ProtocolBl (Businesslogic) habe ich einfach nur ein paar Methode welche mit Protokolle schreiben oder abrufen. Das besondere findest du nun in der Klasse PRopertyChangedHolder.

      Das ist eine Klasse welche ich mir ausgedacht habe. Diese vereint zwei notwendigkeiten welche immer wieder in einer Anwendung benötigt werden. Zum einen ist es notwendig das man sich Änderungen merkt wenn man diese auch Protokollieren möchte. Klar. Aber ich handhabe es auch gerne so das z.b. ein "Speichern" Button erst Enabled wird wenn Änderungen vorhanden sind.
      Ändert der User also den Text von "Test" auf "Test1" wird der Button Enabled. Ändert er nun den Text wieder zurück auf "Test" wird der Button wieder Disabled. Der User hat zwar etwas geändert aber da der Wert wieder zum Uhrsprungwert zurückgesetzt wurde muss ja nicht gespeichert werden. Das nimmt uns die Klasse alles ab.

      MAl sehen:

      VB.NET-Quellcode

      1. Public Class PropertyChangeHolder
      2. Public Sub New()
      3. Changes = New List(Of PropertyChangeItem)
      4. End Sub
      5. Public Sub AddItem(oldVal As String, newVal As String, <CallerMemberName> Optional ByVal propName As String = "")
      6. If Not Changes.Where(Function(p) p.PropertyName IsNot Nothing AndAlso propName.ToLower = p.PropertyName.ToLower).Any Then
      7. Changes.Add(New PropertyChangeItem(propName, oldVal, newVal))
      8. Else
      9. Dim entry = Changes.Where(Function(p) propName = p.PropertyName).Single
      10. entry.NewValue = newVal
      11. End If
      12. End Sub
      13. Public Sub AddItem(item As PropertyChangeItem)
      14. AddItem(item.PropertyName, item.OldValue, item.NewValue)
      15. End Sub
      16. Public Sub AddItems(items As List(Of PropertyChangeItem))
      17. For Each p As PropertyChangeItem In items
      18. AddItem(p.OldValue, p.NewValue, p.PropertyName)
      19. Next
      20. End Sub
      21. Public Sub AddManualText(text As String)
      22. Changes.Add(New PropertyChangeItem(Nothing, Nothing, text))
      23. End Sub
      24. Private Property Changes As List(Of PropertyChangeItem)
      25. Public Function HasChanges() As Boolean
      26. Dim ret As Boolean = Changes IsNot Nothing AndAlso Changes.Count > 0
      27. Return ret
      28. End Function
      29. Public Sub ResetChanges()
      30. Changes.Clear()
      31. End Sub
      32. Public Function GetAllChangedItems() As List(Of PropertyChangeItem)
      33. Return Changes.Where(Function(c) c.OldValue <> c.NewValue).ToList
      34. End Function
      35. Public Function GetAllChangesText(Optional forNewRecord As Boolean = False) As String
      36. Dim changedItems = GetAllChangedItems()
      37. Dim retText As String = ""
      38. For Each c As PropertyChangeItem In changedItems
      39. If forNewRecord Then
      40. retText += $"Neuer Datensatz: '{c.PropertyName }' mit Wert '{c.NewValue }'{Environment.NewLine }"
      41. Else
      42. If c.PropertyName Is Nothing Then
      43. retText += $"{c.NewValue }{Environment.NewLine }"
      44. Else
      45. retText += $"'{c.PropertyName }' wurde von Wert '{c.OldValue }' auf '{c.NewValue }' geändert.{Environment.NewLine }"
      46. End If
      47. End If
      48. Next
      49. Return retText
      50. End Function
      51. End Class
      52. Public Class PropertyChangeItem
      53. Public Sub New(propName As String, oldVal As String, newVal As String)
      54. PropertyName = propName
      55. OldValue = oldVal
      56. NewValue = newVal
      57. End Sub
      58. Public Property PropertyName As String
      59. Public Property OldValue As String
      60. Public Property NewValue As String
      61. Public Overrides Function ToString() As String
      62. Return _
      63. $"PropertyName: {If(PropertyName, "{null}")}, OldValue: {If(OldValue, "{null}")}, NewValue: { _
      64. If(NewValue, "{null}")}"
      65. End Function
      66. End Class



      OK, die Klasse hat eben Methoden wie AddItem, AddItems und AddManualText sowie Methoden wie HasChanged, ResetChanges usw.
      Diese sollten alles selbsterklärend sein und sind denke ich einfach zu verstehen. Jetzt könnte man denken das es ja keine "coole" Arbeit ist immer daran zu denken das man Änderungen immer dort reinpackt. Simpel.

      Da wir unter WPF ja sowieso mit Full-Properties arbeiten da wir im Setter INotifyPropertyChanged werfen müssen können wir ja hier gleich noch was dazupacken.

      Hier ein Auszug aus meiner Klasse NoteVm:

      VB.NET-Quellcode

      1. Public Class NoteVm
      2. Inherits ViewModelBase
      3. ...
      4. ...
      5. Friend ReadOnly Changes As PropertyChangeHolder = New PropertyChangeHolder
      6. Public Sub New()
      7. ...
      8. ...
      9. End Sub
      10. ...
      11. ...
      12. Public Property Title As String
      13. Get
      14. Return _modelNote.Title
      15. End Get
      16. Set
      17. Changes.AddItem(_modelNote.Title?.ToString, Value?.ToString)
      18. _modelNote.Title = Value
      19. RaisePropertyChanged()
      20. RaisePropertyChanged(NameOf(HasChanges))
      21. End Set
      22. End Property
      23. Public Property Content As String
      24. Get
      25. Return _modelNote.Content
      26. End Get
      27. Set
      28. Changes.AddItem(_modelNote.Content?.ToString, Value?.ToString)
      29. _modelNote.Content = Value
      30. RaisePropertyChanged()
      31. RaisePropertyChanged(NameOf(HasChanges))
      32. End Set
      33. End Property



      Ich instanziiere Klassenweit eine Variable Changes und im Setter einer jeden Eigenschaft sorge ich dafür das die änderung dort ankommt.
      Die Klasse PropertieChangedHolder macht den Rest alleine.

      Will ich dann ein oder mehrere PRotokolleinträge schreiben muss ich nur noch über die Hilfsmethoden der Busineslogik diese auf dem Weg schicken. Die Infos für die änderungen habe ich ja bereits gut leserlich in meinem PropertiesChangesHolder verpackt.

      Falls noch Fragen offen sind Frag bitte einfach nach.

      Grüße
      Sascha
      If _work = worktype.hard Then Me.Drink(Coffee)
      Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

      ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

      Hallo,

      erst einmal vielen Dank für deine Antwort.

      Nofear23m schrieb:

      Jaja, die Protokollfunktion. Die ist schon cool wie ich persönlich finde

      Da kann ich dir nur rechtgeben. Ich brauche sie ganz dringend in meinem Projekt.

      Das hat mich dazu gebracht mein (mittlerweile schon recht komplexes Projekt) nochmals umzustellen, auf die Layerart, wie du es machst.
      Dabei ist mir eigentlich auch schon jeder Layer relativ klar, bis auf das Projekt "Repository".
      Das sieht erstens recht komplex aus und zweitens kann ich keinen Sinn sehen.

      Also meine nächste Frage:
      Brauche ich umbedingt das Repository Projekt und was steht da drinn?

      Viele Grüße
      Florian
      ----

      WebApps mit C#: Blazor
      Hallo

      Brauchen tust du es nicht. Dieses Repository ist ein Generisches Repository. Das hat den Vorteil das durch das IGenericRepository die Datenhaltung austauschbar wird. Also wenn ich nun statt EF ein WebService nutzen will un die Daten abzurufen kann ich es einfach austauschen ohne im ViewModel oder der Businesslogic was ändern zu müssen. Weiters ist es sehr praktisch bei UnitTests.

      Braucht man diese Dinge nicht kann mans weglassen.

      Grüße
      Sascha
      If _work = worktype.hard Then Me.Drink(Coffee)
      Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

      ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

      Hallo,

      nur noch mal eine kurze Frage:
      Das GenericRepository ist im Entefekt eine Zwischenschicht zwischen dem DBContext (oder einer anderen Datenquelle) und der BI.
      Wenn ich also nur eine Datenquelle habe, reicht es, wenn ich alle Methoden des GenericResponsidory direkt in die BI packe?

      Viele Grüße
      Florian
      ----

      WebApps mit C#: Blazor
      Hallo

      Du kannst direkt in der BL mit EF Core arbeiten. Also in der BL einen Verweis auf EF Core (Dann muss das BL PRojekt natürlich ein .Net Core Projekt sein) machen und los gehts.

      Grüße
      Sascha
      If _work = worktype.hard Then Me.Drink(Coffee)
      Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

      ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

      Hallo,

      so ich habe mich jetzt die Tage durch das Projekt durchgearbeitet und versucht es an meinem eigenen Projekt anzuwenden.
      Also Funktion hat es noch keine, da ich noch an einem Punkt hänge - und zwar der BI.
      Mir ist bewusst, dass es in der BI einige Methoden (die, die man halt braucht) zum Datenaustausch mit der DB gibt (müssen diese alle Async sein?)
      Allerdings ist mir das "Grundkonzept" nicht ganz klar.

      Du schreibst z.B.

      VB.NET-Quellcode

      1. Friend ReadOnly RepNote As GenericRepository(Of Note)
      2. Friend ReadOnly RepCategory As GenericRepository(Of NotesCategory)

      Dass kann ich nartürlich nicht machen, da ich keine Repository Layer habe...

      Es wäre super nett, wenn du mir anhand der SongBI den GRUNDAUFBAU einer BI ohne Repository zeigen könntest. Also vielleicht eine Methode zum Datenabrufen und den Rest bekomme ich selber hin.
      Das ganze Konzept ist halt schon sehr umfangreich und schwer zu verstehen - aber ich bin motiviert.

      Und wenn dir etwas auffällt, was nicht so gut gelöst ist kannst du es wie immer gerne sagen!

      Viele Grüße
      Florian
      Dateien
      • MusicManager.zip

        (696,27 kB, 105 mal heruntergeladen, zuletzt: )
      ----

      WebApps mit C#: Blazor