Autor Beitrag
JohnDyr
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 56
Erhaltene Danke: 1

Win 10
C# (VS 2017)
BeitragVerfasst: Sa 06.10.18 18:04 
Hallo,

ich bin gerade dabei gewesen eine Anwendung zu bauen, in der mehrere Nutzer gleichzeitig Daten (in "Echtzeit") ansehen und bearbeiten können. Als Grundlage benutze ich dafür einen Microsoft SQL Server. Sobald ein Benutzer einen Datensatz bearbeitet, wird dieser gelocked. Dadurch wird gewährleistet, dass kein anderer Nutzer diesen gleichzeitig bearbeiten kann.

Nun zu meiner Frage: Würdet ihr das auch so machen? Habt ihr generell Tipps die ich bei einer Multiple User Anwendung beachten muss? Da es sich um meine erste handelt, bin ich etwas unsicher mit allen.

Danke,
JohnDyr
Palladin007
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: Sa 06.10.18 20:24 
Du hast mehrere Clients, die gleichzeitig arbeiten können?
Dann würde ich definitiv nicht den Client die Daten ändern lassen, damit hast Du nur Stress.

Schreib eine Server-Anwendung, die die ganze Logik beinhaltet und der Client ruft diese Logik via WCF oder WebAPI auf.
Da Du nur eine Server-Instanz hast, kannst Du dort besser verwalten, was parallel arbeiten darf und was nicht, Du kannst besser cachen, ets.

Außerdem würde ich mit einer RowVersion arbeiten. Wir machen das mit einer DateTime-Column und nutzen NHibernate, diese Version-Column wird bei jedem Save neu geschrieben.
Bei jedem Commit der Transaktion wird geprüft, ob die Version der Datenzeile, die bearbeitet werden soll, noch die gleiche ist, wie zu dem Zeitpunkt, als die Arbeit begonnen hat. Ist die Version eine Andere, dann wurden die Daten zwischenzeitlich geändert und es fliegt ein Fehler.
Wenn dieser Fehler fliegt, dann kannst Du entsprechend reagieren und z.B. die Bearbeitung neu beginnen oder z.B. dem User sagen, er soll es "später" normal probieren.

Ach ja, natürlich sollte alles so gebaut sein, dass jede Klasse über mehrere Threads funktionieren. Multi-Threading-Fehler sind bei so einem Aufbau die Hölle.

Wir haben dafür eine Commands basierte Struktur. Ein CommandDispatcher (oder wie man den nennen will), der die Command-Objekte übergeben bekommt, die vom Client über WCF/WebAPI aufgerufen werden sollen. Der Dispatcher plant die dann irgendwann ein und führt sie aus.
JohnDyr Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 56
Erhaltene Danke: 1

Win 10
C# (VS 2017)
BeitragVerfasst: So 07.10.18 00:07 
Richtig, mehrere Clients (ca. 10) die gleichzeitig arbeiten müssen. Die Logik ist nichts besonders, außer Daten einfügen, ändern, löschen. Also eine klassische CRUD Database.

Zitat:
Außerdem würde ich mit einer RowVersion arbeiten. Wir machen das mit einer DateTime-Column und nutzen NHibernate, diese Version-Column wird bei jedem Save neu geschrieben.
Bei jedem Commit der Transaktion wird geprüft, ob die Version der Datenzeile, die bearbeitet werden soll, noch die gleiche ist, wie zu dem Zeitpunkt, als die Arbeit begonnen hat. Ist die Version eine Andere, dann wurden die Daten zwischenzeitlich geändert und es fliegt ein Fehler.
Wenn dieser Fehler fliegt, dann kannst Du entsprechend reagieren und z.B. die Bearbeitung neu beginnen oder z.B. dem User sagen, er soll es "später" normal probieren.


Verstehe. Der Server wird der Entscheider, die Clients stellen Anfragen. Der Client, welche seine Anfrage zuerst an den Server geschickt gewinnt.

Zitat:
Ach ja, natürlich sollte alles so gebaut sein, dass jede Klasse über mehrere Threads funktionieren


Warum muss die Anwendung Multi Threaded sein? Meinst du die Server Seite, oder Client? Weil ein Client macht kein Sinn, oder? Sorry für blöde fragen, ist meine erste "multiple client server" Anwendung.
Palladin007
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: So 07.10.18 01:00 
Zitat:
Der Server wird der Entscheider, die Clients stellen Anfragen.

Genau.

Zitat:
Warum muss die Anwendung Multi Threaded sein? Meinst du die Server Seite, oder Client? Weil ein Client macht kein Sinn, oder?

Du hast Recht, der Server muss jede Anfrage in einem eigenen Thread abarbeiten können, denn die Anfragen werden von den bekannten Frameworks (z.B. WCF oder ASP.NET WebAPI) asynchron verarbeitet, Du hast also keine Wahl.
Du musst den Server also so bauen, dass er zu jeder Zeit aus jedem Thread heraus jeden Command ausführen kann - oder die Commands so lange warten, bis sie ausgeführt werden können.

Du kannst natürlich die Aufrufe in einen eigenen Thread synchronisieren, allerdings hast Du dann den Nachteil, dass alle Clients warten müssen, wenn nur ein Client eine Anfrage stellt.
Dennoch solltest Du eine Instanz haben, die die Commands überwacht und z.B. auf Fehler reagieren kann.

Zitat:
Der Client, welche seine Anfrage zuerst an den Server geschickt gewinnt.

Dem kannst Du mit Hilfe des CommandDispatchers entgegen wirken. Wenn der bemerkt, dass ein Command dieses "Rennen" verliert (besagter Fehler), dann kann er den Command einfach erneut der Queue hinzufügen, in der Hoffnung, dass der zweite Versuch klappt.
Das solltest Du aber nicht endlos so laufen lassen, wir fangen den Fehler ab dem dritten Versuch nicht mehr ab und zeigen ihn dem User an.

Für diesen Beitrag haben gedankt: JohnDyr
JohnDyr Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 56
Erhaltene Danke: 1

Win 10
C# (VS 2017)
BeitragVerfasst: So 07.10.18 11:41 
Super danke erstmal. Kennst du dazu gute Tutorials, wo so etwas mal durchgenommen wird? Einige habe ich schon gefunden, die die Socket Programmierung erklären (also mehrere Clients kommunizieren mit dem Server). Kennst du zufällig noch gute Tutorials oder Internetseiten, wo Thema erläutert ist?

Und zweite Frage: Damit alle Clients die Datenstruktur immer in "Echtzeit" sehen, habe ich ein Polling Mechanismus angelegt, der nach einem Intervall immer die Tabelle auf der Form aktualisiert. Finde ich selbst nicht so schön, aber was besseres viel mir da nicht ein. Wie löst man sowas normalerweise elegant?
Palladin007
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: So 07.10.18 14:55 
Ich kenne keine Tutorials, aber ich kann dir sagen: Lass die Socket-Programmierung weg :D Das haben Andere besser gemacht, als Du
Ich hab ja schon ein paar Mal WCF oder die WebAPI erwähnt, die sing genau dafür da, dass mehrere Prozesse untereinander kommunizieren können, wie z.B. viele Clients mit einem Server.
Meiner Meinung nach ist WCF für Anfänger einfacher, da hier beide Seiten (Server und Client) ähnlich einfach aufgebaut sind. Allerdings hast Du mit der WebAPI den Vorteil, dass sie auf REST aufbaut und das ist global anerkannt und es gibt für wahrscheinlich jede große Programmiersprache eine Implementierung. Außerdem ist unklar, was mit WCF in der Zukunft passiert, ob MS das noch weiter entwickelt oder nicht.

Jedenfalls gibt's für Beides mehr als genug Tutorials und Beispiel-Projekte als Du lesen kannst.


Wenn Du auf den Clients die Daten in Echtzeit aktualisieren willst, kann ich dir keinen Best-Practice bieten, aber Ideen, wie ich das anfangen würde:

Eine Schleife, die in einem eigenen Thread ständig beim Server nach fragt, ob es Änderungen gibt. Der antwort jedes Mal mit Nein, bis es tatsächlich eine Änderung gibt und der Client kann dann entsprechend reagieren.
Das hat den Vorteil, dass es sehr einfach ist, allerdings hast Du immer eine Latenz mit drin, nämlich die, die Du zwischen jeder Anfrage wartest und das hast Du allein schon wegen dem Verbindungs-Aufbau.

Die zweite Variante ist eine Anpassung der ersten Variante, ich kenne es unter "Long Polling".
Der Client hat genauso eine Schleife, allerdings verhält sich der Server etwas anders. Der Server, der wartet nämlich einfach so lange, bis kurz vor dem Timeout der Anfrage und antwortet erst dann mit Nein.
Wenn auf dem Server nun eine Änderung registriert wird, muss der dafür sorgen, dass eine offene Anfrage nicht mehr wartet, sondern sofort Ja zurück gibt. Der Client bekommt dann sofort die Antwort und kann entsprechend reagieren.
Das Ergebnis ist eine Art Event-System, wie der Client über Ereignisse vom Server in beinahe Echtzeit informiert werden kann.
Der Nachteil ist, dass das Management auf dem Server etwas aufwändiger ist, allerdings ist der Server nicht wie bei Variante 3 direkt vom Client abhängig.

Die andere Option ist, dass der Server direkt eine Verbindung mit dem Client auf nimmt, der also auch eine Art Server ist, während der Server die Rolle des Clients einnimmt. Beim Start registriert der Client die Informationen zum Verbindungs-Aufbau beim Server. Allerdings kann das bedeuten, dass zusätzliche Installations-Schritte auf dem Client nötig sind. Bei WCF (bei webAPI weiß ich es nicht) muss z.B. immer die URL mit Port auf dem PC registriert werden (netsh http add urlacl url=... user=...) und das fordert Admin-Rechte.
Gerade, wenn der Server sehr komplexe Daten zu sehr unterschiedlichen Zeiten übermitteln soll und ein Long Polling-System zu aufwändig wäre, könnte das lohnenswert sein.
Ich persönlich würde das in deinem Fall nicht machen, denn Du willst ja nur über Änderungen informiert werden?


Meiner Meinung nach ist für sowas die Option 2 die Beste, damit hast Du den schnellsten Informations-Fluss, ohne Abhängigkeit des Servers vom Client.
Wenn Du das machen willst, dann schau dir das ManualResetEvent an, der schwierigste Teil der Arbeit für den Server wird darin schon fertig mit geliefert.

Für diesen Beitrag haben gedankt: JohnDyr
JohnDyr Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 56
Erhaltene Danke: 1

Win 10
C# (VS 2017)
BeitragVerfasst: So 07.10.18 15:22 
Danke für die Antwort. Ich habe die erste Variante implementiert, da diese am einfachsten ist^^

Die Zweite ist natürlich besser und wäre eine Optimierungsmöglichkeit. Klingt allerdings etwas aufwändiger in der Umsetzung. Dennoch schaue ich mir diese mal an!

Thx.
JohnDyr Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 56
Erhaltene Danke: 1

Win 10
C# (VS 2017)
BeitragVerfasst: Mo 08.10.18 09:22 
Ich habe nochmal eine etwas "blöde" Nachfrage: Es sind also tatsächlich zwei getrennte Anwendungen die ich programmieren müsste? Einmal den Client und einmal den Server? Dabei stellt der Server eine REST API bereit, mit der Client und Server kommunizieren? Korrekt, oder?
Palladin007
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: Mo 08.10.18 12:21 
Genau

Wobei es keine REST-API sein muss, WCF liefert ja ein eigenes SOAP-FOrmat mit. Wenn man die API aber langfristig auch für externe Anwendung öffnen können soll, wäre es besser, von Anfang an auf REST zu setzen, denn damit kann jeder was anfangen. Es ist zwar auch möglich, mit WCF eine REST-API anzubieten, aber da würde ich dann doch lieber auf die WebAPI von ASP.NET (Core) setzen, die wurde genau dafür gebaut.

Was Du nimmst, musst Du entscheiden. Die Server-Seite ist bei Beidem ähnlich kompliziert (bzw. einfach), auf Client-Seite gibt es aber deutliche unterschiede. WCF liefert für den Client direkt die nötigen Klassen mit, die dir dann eine Proxy-Implementierung von dem Interface bieten. Die WebAPI ist nur Server, da brauchst Du dann noch ein zusätzliches Framework für den Client. Es gibt da z.B. Refit, was einen Großteil zur Compile-Zeit automatisch generiert, oder RestSharp, was sehr viel genauere Arbeit erlaubt, allerdings musst Du dann auch den Großteil der Kommunikation selber implementieren.
JohnDyr Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 56
Erhaltene Danke: 1

Win 10
C# (VS 2017)
BeitragVerfasst: Mo 08.10.18 13:39 
Ich habe mir mal die WebAPI angeschaut und für mich schaut es eher aus, als wäre diese für Webprojekte gedacht. Dabei will ich ja eine lokale Anwendung programmieren.

Zweite Frage: Ist ein Server eigentlich zwingend notwendig? Bei der Anwendung geht es nur um ein kleines Tool für einen bekannten, d.h. es läuft lokal in seinem Netzwerk. Der Client kann doch auch direkt mit der Datenbank sprechen, da er über den Connect String die Verbindung zur DB herstellt. Also hätte ich praktisch nur eine Client <-> Datenbank Schnittstelle. Mir ist bewusst, dass dies sicherheitskritisch ist, aber wenn es sich nur um ein kleines Tool handelt...? Einen Server mit einer REST API zu entwickeln würde ich zwar aus Lerngründen schon machen, aber in dem Projekt erscheint es mir als unnötiger Overhead. Meinung?
Palladin007
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: Mo 08.10.18 14:35 
Die WebAPI kommt auch aus der Web-Richtung, immerhin ist es Bestandteil von ASP.NET.
REST ist ja im Prinzip auch nichts anderes als HTTP-Calls und die Daten werden z.B. in der URL mit gegeben.

Genauso ist aber auch ein Web-Service diese "Web-Richtung" und nichts anderes ist besagter Server.
ASP.NET mag im ersten Moment viel zu übertrieben klingen und das mag je nach Betrachtungsweise auch stimmen, aber es ist aktuell der Platzhirsch, wenn es um REST-Services im .NET-Bereich geht.

Und ob ein Server notwendig ist:
Das kann ich dir nicht sagen.
Ein Server bietet den Vorteil, dass Du nur eine Anwendung hast, die in den Daten schreibt. Hast Du mehrere Clients, musst Du auch pro Client davon ausgehen, dass die einzelnen Clients sich beim Schreiben der Daten stören können: Client A schreibt Daten, kurz darauf schreibt Client B ebenfalls Daten, allerdings auf Grundlage eines veralteten Datenbestandes, was dann zu falschen Daten führen kann.
In einer einzelnen Anwendung kannst Du das besser überwachen und auf entsprechende Situationen reagieren, sind es viele Anwendungen, geht das nicht mehr.
Ein weiterer Vorteil von einem zentralen Server ist, dass er eben zentral ist, als Admin kannst Du dort Dinge einstellen, Updates einspielen, etc., ohne dass die Clients davon etwas mit bekommen.

Wenn es wirklich nur kleiner, privater Bereich ist, dürfte es wahrscheinlich auch reichen, pro Tabelle eine Version-Spalte festzulegen und das ORM prüft die dann bei jedem Commit.


Ich persönlich finde aber, dass der Aufwand von so einem Web-Service sich in Grenzen hält. Die Logik darin brauchst Du so oder so (wäre ja sonst im Client), dazu fehlt noch der Web-Service (mir WCF und etwas Übung eine Sache von ein paar Minuten) und was es zusätzlich an Management auf dem Server gibt, wie z.B. das ausführen der Commands. Letzteres wird das Aufwändigste sein.
Die ganz Faulen können aber auch einfach alles nebenläufig ausführen oder eben nichts (mit lock), dann sind deine Commands nur eine Interface-Implementierung mit einer Execute-Methode und der "CommandDispatcher" eine Methode, die diese Execute-Methode aufruft, wahlweise in einem lock oder eben nicht.

Für diesen Beitrag haben gedankt: JohnDyr