Laden...

Bester Weg für einen Excel Import über Web Api

Erstellt von Rioma vor 7 Jahren Letzter Beitrag vor 7 Jahren 4.393 Views
R
Rioma Themenstarter:in
228 Beiträge seit 2013
vor 7 Jahren
Bester Weg für einen Excel Import über Web Api

Hallo zusammen,

ich hätte eine kurze Frage: Die Anforderung ist es, Excel-Dateien in einer Datenbank zu importieren. Das ganze muss von extern funktionieren.

Bisher wurden die Excel-Dateien nur über eine Web Api hochgeladen(In Chunks).

Mich würde interessieren, welcher der x-tausend Wege eurer Meinung nach am besten geeignet wäre:

**Nachdem die Dateien hochgeladen sind, dem Benutzer die Möglichkeit geben den Import anzustoßen (pro Datei anstoßen, oder einmal anstoßen für alle?). Daraus folgt: **

--> Request-Verbindung offen halten und dann einen Statuscode zurückgeben. Wenn einmal anstoßen für alle, müsste man noch entsprechend zurückgeben, welche Datei einen eventuellen Fehler hatte. Außerdem müsste die Timeout-Zeit wahrscheinlich relativ hoch sein.

--> Anstoßen und Ok zurückgeben für Start des Imports(Request-Ende). Intern dann vielleicht in einem Dataflow die Dateien abarbeiten. Nur wie bekommt der Benutzer eine Rückmeldung? Polling und gucken ob abgeschlossen? Wenn ja, wie sollte der Dataflow-Service aufgebaut sein? Als Singleton für alle Benutzer, oder jedes mal eine neue Instanz und entsprechend neue Threads (eher ungünstig für Web Api).

--> SingalR für "Live-Aktualisierung" des Imports. Bisher wird SignalR aber in der Web Api nicht gebraucht. Live-Aktualisierung beim Client ist kein muss.

Oder auf den Chunk-Upload verzichten?

--> Dann direkt den Import nach Upload starten.

Ich hoffe ihr könnt meinen Gedankengängen folgen und mir den richtigen Weg zeigen.

Danke

16.806 Beiträge seit 2008
vor 7 Jahren

Verbindung offen halten wird in 90% der Zeit schief gehen. Wenn es länger dauert, dann ist die Gefahr groß, dass der Benutzer irgendwann einfach das Fenster (ausversehen) schließt.
Zudem soll man i.d.R. nichts im WebAPI Prozess ausführen, das länger als 10-30 Sekunden dauert. Hier währen wir beim Thema Architektur und Microservices.

Besserer Weg ist definitiv das Pipelining.

  • Dokument entgegen nehmen
  • Dokument zB. an einen Windows Service übergeben, der den Import übernimmt
  • Status-Nachrichten über WebSockets (zB SignalR) raus geben.

SignalR wäre dann auch nicht innerhalb der WebAPI, sondern eben im Rahmen eines Microservices eine eigenständige Instanz.
Ich nenne das gerne einfach nur PushService.

Trennung zB
host/ExcelApi/v2/Upload host/PushAPI/v2

Singeltons haben in WebAPIs nichts verloren.
Eine WebAPI sollte logisch gesehen immer stateless sein.

R
Rioma Themenstarter:in
228 Beiträge seit 2013
vor 7 Jahren

Da das ganze auf Asp.Net Core umziehen soll, wäre Plattformunabhängigkeit ganz schön. Daher würde der Windows-Service rausfallen (auf Mono würde ich gerne verzichten)
Oder einmal ein Windows-Service für Windows und eine Anwendung für Linux (Asp.Net Core-> Konsole?).

Wie würdest du mit dem Windows-Service kommunizieren? Wenn über SignalR Live-Aktualisieren rausgehen sollen, müsste ich diese vom Serivce bekommen. IPC --> WCF?

EDIT: Wie am besten mit der Linux-Anwendung kommunizieren?
komplett auf WebSockets setzen (Linux und Windows)?

Ablauf:

  1. Hochladen über Web Api
  2. Über SignalR zum Backend-Verbinden
  3. dann den Import starten (oder Import über Web Api starten?)
  4. Verbindung trennen

Da SignalR sonst nicht gebraucht wird, würde ich nur dafür die Verbindung öffnen und dann wieder schließen.

SignalR und WebApi in einer Anwendung, mit verschiedenen URL's, oder hier auch nochmal splitten?
(Mir persönlich wäre es ein bisschen viel 3 Anwendungen zu haben, für einen "kleinen" Import.)

Vielen Dank

16.806 Beiträge seit 2008
vor 7 Jahren

Spielt keine Rolle ob Windows Service oder irgendein Äquivalent.

Lässte die WebAPI halt in nem Docker Contailer laufen. Zusätzlich haste ne .NET Core App für den Import selbst in nem anderen Docker Container.
Die Excel nimmste in der WebAPI an, legst diese in einen Ordner, auf den beide Docker Container Zugriff haben. Die Import App nimmt die Excel Datei entgegen und verarbeitet sie - fertig.

Entsprechendes Prinzip hat Rainer Stropek auf der Basta gezeigt
Docker für ASP.NET-Entwickler, bringt das was? (Position ca. Minute 55)

SignalR und WebApi in einer Anwendung, mit verschiedenen URL's, oder hier auch nochmal splitten?
(Mir persönlich wäre es ein bisschen viel 3 Anwendungen zu haben, für einen "kleinen" Import.)

Im Sinne der Microservice Architektur separierst Du Anwendungen nach ihren Aufgaben.
Es ist auch viel leichter zu konfigurieren, es zu warten und auch auszurollen. Mit Docker dann sowieso.

Kannst auch alles in eines packen - finde ich (auch aus Erfahrung) aber nicht gut.
Ich separiere es mittlerweile für die simpelsten Dinge, einfach weil auch die Wiederverwendbarkeit viel viel höher ist.

R
Rioma Themenstarter:in
228 Beiträge seit 2013
vor 7 Jahren

Danke erstmal. "Eine" Frage hätte ich aber noch zur Architektur:

Die Web Api übernimmt den Upload und schiebt die Datei in einen Ordner.

Gibt es hier nun 2 weitere Anwendungen oder eine? SignalR für Kommunikation zum Client und Anwendung für den Import? Oder nur die SignalR Anwendung, die auch den Import übernimmt?

Sollte es 2 Anwendungen geben, wäre meine Frage, wie die Anwendungen miteinander "sprechen". Auch wieder SignalR? Da sonst der Client keine Informationen bekommt.

16.806 Beiträge seit 2008
vor 7 Jahren

Ich sag Dir mal, wie ich da eigentlich vorgehe; habe derzeit eine ähnliche Anforderung für ein Projekt für den führenden Hersteller von Kettensägen.

Täglich wird von einem externen System eine Datei mit rund 130 MB an Informationen hochgeladen.
Diese Datei muss ich also entgegennehmen, strukturell validieren, parsen, inhaltlich validieren, verarbeiten und Werte in die Datenbank eintragen oder aktualisieren.

Gleichzeitig wird über eine WebAPI REST-mäßig eben CRUD-Befehle ausgeführt.
Eine weitere Anforderung ist bei mir das administrative Verwalten der Einträge sowie Reporting.

Ich habe also eine Anwendung, die nur den Import entgegen nimmt.
Eine Anwendung für die CRUD-Ausführung.
Eine Anwendung für administrative Dinge und eine für die Reports.

Warum trenne ich das?

  1. Skalierung: die CRUD-Befehle sind viele Millionen am Tag. Report wird aber nur 1 mal die Woche bedient.
  2. Authentifzierung: Admin und Report dürfen nur intern aufgerufen werden. Für CRUD gibt es ein Token-System
  3. Single Responsibility: bei Microservices ist es so, dass die Anwendungen vollständig getrennt sein müssen. Das bedeutet unterschiedliche Umgebung, unterschiedliche Deployments.
    Die dürfen sich alle ähneln, sogar die gleichen sein. Aber wenn ich Funktion A aktualisiere, will ich nicht, dass etwas anderes dadurch in Mitleidenschaft gezogen wird.

Aber wenn ich was am Reporting ändere, dann muss ich nicht die hoch-prio API abschalten für den Deployment Prozess.
Wenn das Reporting kracht oder mal ne Woche nicht verfügbar ist: stört hier niemanden.
Wenn das Hochladen mal eine Nacht nicht geht, oder auch hier was geändert werden muss: gut, dann ist das so. Hauptsache die CRUD-API läuft.

Kommunizieren tun Microservices entweder durch direkte Schnittstellen, oder durch einen Message Bus.
Das kommt drauf an.

Azure selbst intern nutzt zB. REST APIs untereinander.
Deswegen gibt es bei Microsoft intern auch die Regel, dass ein Breaking Change einer REST API eines Services mindestens 1 Jahr vorher schriftlich angekündigt werden muss.

In Deinem Fall würde ich wahrscheinlich ebenfalls vier Anwendungen haben:

  • eine Notification App, die via WebSockets Push zur Verfügung stellt
  • eine WebAapi für das hochladen der Datei
  • eine WebApi für CRUD
  • eine .NET Core App mit einem Watcher für die Excel Datei für die Verarbeitung

Das hört sich alles erst mal viel an:
aber mit entsprechend guter Aufteilung der Projekte in einer Solution und einem guten Continuous Integration Design ist das alles echt ein Kinderspiel und dann nur noch eine Sache von Minuten.
Gleichzeitig bist Du aber enorm flexibel.

Die Upload API nimmt also die Excel entgegen und schiebt sie in einen Ordner.
Wenn das Verschieben schief geht, dann wird über den Push-Service der Benutzer informiert, dass es schief ging.

Hat es geklappt, dann erkennt die .NET Core Import App die neue Excel-Datei und verarbeitet diese.
Statusnachrichten werden hier ebenfalls über die PushAPI an den Benutzer verteilt.

Die CRUD-API selbst sollte keine/so wenig wie auch nur möglich Abhängigkeiten an andere Services haben (meine Ansicht/Meinung!).
Das ist alles nur eine Idee für Dich und eine Erklärung, wie ich i.d.R. vorgehe. Du kannst das auch entsprechend vereinfachen.

R
Rioma Themenstarter:in
228 Beiträge seit 2013
vor 7 Jahren

Vielen Dank für die ausführliche Antwort. Sehr nachvollziehbar und hilfreich. Ich werde es nun erstmal wie von die beschrieben aufziehen und gucken ob ich auf Hindernisse stoße.

Danke

EDIT: Wenn du von einem "Watcher" redest

eine .NET Core App mit einem Watcher für die Excel Datei für die Verarbeitung

meinst du den FileSystemwatcher? Ich hatte bisher nie die Anforderung diesen zu benutzen, habe allerdings gehört, dass dieser nicht unbedingt zuverlässig sein muss.

16.806 Beiträge seit 2008
vor 7 Jahren

Da gibts ja viele Wege, wie Du die Verarbeitung des Imports anstößt.
FileSystemwatcher ist glaube ich bei .NET Core auch gar nicht verfügbar. Daher fällt der eh raus.

Aber Du könntest zB. in dem Import-Docker-Container nen NodeJS laufen lassen (der kann Watchen, siehe Rainers YouTube Video).
NodeJS informiert dann Deine .NET Core App oder startet diese an und fertig.

Das schöne bei solch einer Architektur ist ja, dass Du das jederzeit anpassen und Technologien einfach mixen kannst, wie eben die Anforderungen wachsen 😃
Ich hab auch nen NodeJS Container, der via chokidar auf nen Ordner hört und diese dann via HTTP Post an einen externen Service weiter reicht.

Das ist alles super flexibel; aber ich versteh, dass man sich an diese super Flexibilität erstmal gewöhnen muss 😃

R
Rioma Themenstarter:in
228 Beiträge seit 2013
vor 7 Jahren

Danke nochmal, dass mit NodeJS war mir nicht bewusst. Ich werde mir das Video natürlich ansehen, werde nur leider erst abends dazu kommen.

Bezüglich dem FilesystemWatcher hatte ich kurz gesucht und folgendes gefunden:

https://stackoverflow.com/questions/33902230/cannot-use-filesystemwatcher-with-coreclr-on-ubuntu

Daher war ich davon ausgegangen, dass es geht.

Das heißt, dein Pushservice sendet die "Nachrichten" von X Programmen über Websockets raus. Die Nachrichten, die verschickt werden, kommen von diesen Programmen zum Beispiel über Rest-Calls rein? So wäre das ganze um beliebig viele Programme erweiterbar.

Clientseitig müsste ich dann nur dafür sorgen, dass die Nachrichten richtig verteilt werden, oder? (Angular-Controller X für Excel, Angular-Index-Controller für Nachrichten vom Typ Y)
Sei es nun eine Angular oder Desktop-Anwendung. (Ich gehe davon aus, dass die Verbindung einmal aufgebaut wird und dann bis zum Logout bestehen bleibt)

Spricht etwas dagegen, die Nachrichten per Broadcast in einer Angular-App an die Controller zu verteilen? Oder ist das zu ineffizient?

16.806 Beiträge seit 2008
vor 7 Jahren

Ja gut, kann gut sein, dass der FSW mittlerweile unter CoreCLR verfügbar ist.

Die Nachrichten, die verschickt werden, kommen von diesen Programmen zum Beispiel über Rest-Calls rein?

Bei einer Anwendung mach ich das so. Auf Azure benutze ich dafür i.d.R. den Service Bus als Queue.
Das hilft mir noch mehr modular zu sein. Sollte der PushService kurz nicht zur Verfügung stehe geht die Nachricht nicht verloren.

Clientseitig müsste ich dann nur dafür sorgen, dass die Nachrichten richtig verteilt werden, oder?

Si.

Wie man aber das Verteilen macht (im Controller, eigener ng Service...) kann man pauschal nicht sagen.
Auch hier gibts es viele Wege.
Prinzipiell sollte man aber Toasts so designen, dass sie auch ankommen, wenn man mittlerweile in einem anderen Controller ist. Sonst macht es ja wenig sinn.

R
Rioma Themenstarter:in
228 Beiträge seit 2013
vor 7 Jahren

Alles klar, vielen Dank. Deine Hilfe war sehr Aufschlussreich 😃

16.806 Beiträge seit 2008
vor 7 Jahren

Noch ein Zusatz zur Queue für den PushService:

Wenn ein Service eine Nachricht verschicken soll, dann schreib ich nur in die Queue das Nachrichtenobjekt; der Service muss gar nicht wissen, wer die Nachricht nachher absendet

Hab das auch so (nicht bei allen Anwendungen) implementiert, dass der PushService bei manchen Nachrichten schaut, ob der Benutzer aktuell überhaupt via WebSocket/App verbunden ist.
Wenn nicht, dann geht es (zusätzlich) als E-Mail oder je nach Priorität als SMS raus und geht so nicht verloren.
Die Logik muss der sendende Service ja alles gar nicht kennen. Is dem ja egal. Der will nur senden.

Sollte es einen neuen Nachrichtenkanal (zB Rauchzeichen) geben, dann muss nur der PushService geändert werden, und nicht jede sendende Logik in anderen Services.

Das Senden der E-Mail könnte aber wieder ein anderer Service sein und muss nicht unbedingt direkt die WebApp sein, die für WebSockets zuständig ist.

R
Rioma Themenstarter:in
228 Beiträge seit 2013
vor 7 Jahren

Das heißt dein Pushservice hat eine Queue die abgearbeitet wird? Die Queue hält mehrere Nachrichtenobjekte und die wiederum Ziel-Client, Nachricht usw.

Sämtliche Programme die Nachrichten rausschicken möchten, befüllen die Queue über Rest-Calls mit Nachrichtenobjekten.

Enthält das Nachrichtenobjekt noch eigene Logik? Zum Beispiel:

Interface INachricht mit Methode Send();

App X hat das Nachrichtenobjekt X und eigene Logik zum Versenden. App Y genauso, Client Z reicht das allgemeine Nachrichtenobjekt mit Standard-Logik

Die Queue würde dann über INachricht laufen.

So müsste eine neue Nachricht (mit neuer Funktion) nur im Pushservice implementiert werden.
Oder verstehe ich etwas falsch?

EDIT: Ich glaube das war quatsch. Um verschiedene Nachrichtenobjekte an die RestApi zu senden, bräuchte man entweder ein Custom-Modelbinder oder verschiedene Routen.... Ich warte besser erstmal auf deine Antwort.

16.806 Beiträge seit 2008
vor 7 Jahren

Mein Nachrichtenobjekt enthält i.d.R. ne ID, ne Prio, Absendersystem, Empfänger, Datum und die Nachricht selbst, wobei diese selbst wiederum ein Generic sein kann, sodass es einmal nur ein Text ist oder zB HTML.

Die Nachricht in der Datenbank hat dann noch die Info, wann die Nachricht gelesen wurde, wobei diese nur gesetzt wird, wenn der Benutzer die Nachricht in der Oberfläche (Website oder App) gelesen hat zB. der Benutzer die E-Mail Bestätigung bei sich (zB in Outlook aktiviert hat).
So verhinder ich, dass der Benutzer mehrfach die gleiche Meldung liest so gut es eben technisch geht.

Nachts gibt es nur eine Instanz des Push Service, der Nachrichten aus der Queue nimmt; tagsüber durch die Skalierung auch bis zu 5.

Nachrichten selbst sind aber nur dumme Containerobjekte.

R
Rioma Themenstarter:in
228 Beiträge seit 2013
vor 7 Jahren

Alles klar. Da mir leider kein Azure zur Verfügung steht: Wie würdest du die Queue implementieren? Auf Basis einer Blocking-Collection wie auf deinem Blog gezeigt? Ich setze diese Logik bereits bei einem Logging-Service ein und hatte bisher ganz gute Erfahrungen damit.

16.806 Beiträge seit 2008
vor 7 Jahren

Eine BlockingQueue hilft Dir nur die Abhängigkeiten zu minimieren, was schon mal wichtig ist.
Für persistente Queues, die Nachrichen zB in eine DB speichern, damit sie bei einem Absturzt nicht verloren gehen, ist das aber nichts, da brauchst Du zB. RabbitMQ.

Kommt also drauf an, was Deine Anforderungen hier sind.
Du kannst die Persistenz aber selbst (zB. mit Hilfe von LiteDB) implementieren. Hab ich bei einem Demoprojekt gemacht. In-Process DBs sind für größere Anwendungen und Redundanz aber nichts.

Wir bewegen uns langsam in ne Richtung, bei der es nun konkret um Deine Implementierung geht - weniger um das grundsätzliche Vorgehen.
Ab nem gewissen Punkt kann ich Dir nicht mehr nicht-pauschal antworten. Wie gesagt; da gibt es dann viele Möglichkeiten.
Da musste dann einfach selbst überlegen und schauen, was in Deiner Umgebung umsetzbar ist bzw. wie die Äquivalente bei OnPremise ausschauen.
Auch hat so eine Architektur einen gewissen Overhead, den ihr eben evaluieren müsst.

R
Rioma Themenstarter:in
228 Beiträge seit 2013
vor 7 Jahren

Ja du hast recht. Vielen Dank nochmal für deine hervorragende Hilfe.

Ich werte mir RabbitMQ mal ansehen.

R
Rioma Themenstarter:in
228 Beiträge seit 2013
vor 7 Jahren

Danke nochmal für die ausführliche Hilfe.

Eine Frage ist mir mittlerweile noch gekommen. Wie würdest du unterschiedliche Sprachen handhaben?

Die Services, die die Nachrichten über den Pushservice rausschicken möchten, wissen ja so erstmal nichts von der Client-System-Sprache und immer ist das vielleicht auch nicht möglich. In der Hand des Pushservices sollte dies wohl auch nicht liegen die Nachrichten zu "übersetzen".
Sollte das Übersetzen beim Client liegen, könnten nur standardisierte Nachrichten rausgeschickt werden wie zum Beispiel: "UploadStatusOk" wird zu "Datei erfolgreich hochgeladen" oder eben das ganze auf Englisch.

Ich drehe mich momentan ein wenig im Kreis. Vielleicht hättest du ja nochmal einen Tipp.

Danke

16.806 Beiträge seit 2008
vor 7 Jahren

Auch hier führen viele Wege nach Rom.
Kann man beides machen. Gibt aber genug Tutorials dazu, zB auf http://asp.net oder Blogs wie von in der ASP.NET Szene bekannten Damian Bod WEB API LOCALIZATION 😉

_
227 Beiträge seit 2006
vor 7 Jahren

Hallo,

verstehe ich das richtig, dass nach der Aufteilung in die verschiedenen Anwendungen, jede getrennt (sei es im IIS, Self-Hosted Console oder Windows Server) irgendwie laufen muss? Das heißt bei einer umfangreicheren Anwendung da schon so einige Services zusammen kommen?
Dabei kann die Kommunikation entweder über REST oder einen ServiceBus erfolgen?

Gibt es Frameworks, welche das Hosten quasi übernehmen und man nur noch die Services "reinwirft"?

Habe ich nun eine Anwendung mit x- Services sowie einer ASP.NET MVC Seite als Frontend, bietet es sich nun an, dass die ASP.NET MVC Anwendung auf die Services zugreift oder der Client direkt auf die APIs?

Ich weiß, dass es da viele richtige Möglichkeiten gibt, wäre aber für ein bisschen input dankbar.

16.806 Beiträge seit 2008
vor 7 Jahren

Das heißt bei einer umfangreicheren Anwendung da schon so einige Services zusammen kommen?

Ja, sowas nennt man im Prinzip Microservices und im Sinne eines ganzen Ökosystems dann Serverless Architecture.

Dabei kann die Kommunikation entweder über REST oder einen ServiceBus erfolgen?

Clients sollten immer via REST mit Services kommunizieren; und wenn möglich mit einem entsprechenden Hypermedia Protokoll.

Gibt es Frameworks, welche das Hosten quasi übernehmen und man nur noch die Services "reinwirft"?

... Docker?
Ansonsten weiß ich nicht, was Du genau meinst.

Habe ich nun eine Anwendung mit x- Services sowie einer ASP.NET MVC Seite als Frontend, bietet es sich nun an, dass die ASP.NET MVC Anwendung auf die Services zugreift oder der Client direkt auf die APIs?

Beides möglich.

_
227 Beiträge seit 2006
vor 7 Jahren

Das heißt bei einer umfangreicheren Anwendung da schon so einige Services zusammen kommen?

Ja, sowas nennt man im Prinzip Microservices und im Sinne eines ganzen Ökosystems dann Serverless Architecture.

Konkret hätte ich also je Service ein Projekt in Visual Studio?
Hat man da bestenfalls noch ein Shared Assembly zu jedem Service um nicht in jedem anderen Projekt das den Service nutzt entsprechende Model Klassen duplikate zu haben?

Gibt es Frameworks, welche das Hosten quasi übernehmen und man nur noch die Services "reinwirft"?
... Docker?
Ansonsten weiß ich nicht, was Du genau meinst.

Docker ist natürlich eine gute Möglichkeit, zumindest für Asp.NET Core MVC. Geht das auch mit Subfolder /service1 /service2 usw. oder nur mit Proxy?

Möchte ich das nun aber im IIS beispielsweise hosten (Asp.NET MVC 5), bräuchte ich für jeden Service den ganzen Overhead einer eigenen Website (im IIS), richtig?
Wäre das dann nicht overkill für low traffic Anwendungen/Websites (sind wohl ca 98%)?

16.806 Beiträge seit 2008
vor 7 Jahren

Konkret hätte ich also je Service ein Projekt in Visual Studio?

Kann, muss aber nicht.

Hat man da bestenfalls noch ein Shared Assembly zu jedem Service um nicht in jedem anderen Projekt das den Service nutzt entsprechende Model Klassen duplikate zu haben?

Wenn sich das ergibt natürlich; aber im Prinzip ist jeder Microservice seine eigene Umgebung.
Kommuniziert wird über Service Bus oder APIs.

Docker ist natürlich eine gute Möglichkeit, zumindest für Asp.NET Core MVC. Geht das auch mit Subfolder /service1 /service2 usw. oder nur mit Proxy?

Geht alles. Kann man beides machen.
Kommt aber alles auf die Gesamtumgebung an.

Möchte ich das nun aber im IIS beispielsweise hosten (Asp.NET MVC 5), bräuchte ich für jeden Service den ganzen Overhead einer eigenen Website (im IIS), richtig?

Korrekt. Kann man aber super automatisieren.

Wäre das dann nicht overkill für low traffic Anwendungen/Websites (sind wohl ca 98%)?

Nope.