Der letzte Punkt war im April 2024 am extremsten: der AnthropicAI Crawling Bot hat 94% des Traffics des Forums verursacht. Das ist wahnsinnig.
Wenn Du das im User Agent sehen kannst, wäre es nicht eine Option, solche Requests zu blockieren?
Ist irgendwo ein Tropfen auf den Heißen Stein, weil aufzuhalten ist das sicher nicht mehr, aber so verursacht der Crawler wenigstens keine Kosten.
Mir ist nicht klar, was Du da machst.
WPF hat nichts mit SQL zu tun, der Name, den Du in WPF verwendest, ist eine C# Property und in SQL steht der Name der Column. Das sind zwei völlig verschiedene Dinge, die gleich sein können (z.B. EFCore verwendet standardmäßig den Property-Namen als Column-Namen), aber es in der Regel nicht sind.
Also: Was machst Du überhaupt?
Raten bringt niemandem irgendetwas und nur Code ist eindeutig.
In deinem Code sind keine eckigen Klammern?
Wenn Du eine Frage zu konkretem Code hast, solltest Du vielleicht diesen Code zeigen 😉
Ich würde mal raten, Du redest nicht von WPF (oder generell XAML) sondern von eckigen Klammern in SQL?
Dort haben die nämlich eine Escape-Funktion, zwischen eckigen Klammern kann der Spaltennamen fast beliebig lauten, ohne eckige Klammern sind die Regeln aber strenger - wie z.B. kein Punkt, da der eine weitere Bedeutung hat.
Wenn die Spalte also im Namen mit einem Punkt endet, dann wirst Du dafür immer eckige Klammern brauchen.
Bei anderen Spalten ohne Sonderzeichen sind die eckigen Klammern nicht notwendig.
Lies dir doch Mal deine Frage durch und stell dir vor, Du bist ein potentieller Helfer und hast keine Ahnung, das das Projekt ist?
Welches UI-Framework?
Wie sieht der async/await Code aus?
Und wie man das löst hängt stark vom Kontext ab.
Bei einer dauerthaft im Hintergrund-/ThreadPool-Thread arbeitende Funktion muss die Aktualisierung in den UI-Thread synchronisiert werden, dafür gibt's meistens entsprechende Methoden.
Und wenn die Arbeit async stattfindet (z.B. HTTP-Requests), dann muss man das in der Regel nicht beachten, da die Synchronisation automatisch stattfinden - vorausgesetzt, Du nutzt es richtig.
Ggf. hilft dir Microsoft.Extensions.FileProviders weiter?
Das ist eigentlich im Rahmen von ASP.NET Core entstanden, kann man aber auch unabhängig davon nutzen, such einfach bei NuGet nach "Microsoft.Extensions.FileProviders".
Das ist eine rein lesende Abstraktion ums Dateisystem, kann also nur lesende Streams öffnen, aber keine Ordner oder Dateien erstellen, bearbeiten, umbenennen, etc.
Wenn es nur ums Öffnen und Lesen von Dateien geht, sollte das aber völlig ausreichen.
Wenn Du auch Dateien bearbeiten musst, dann kannst Du ggf. darauf aufbauend die Abstraktion ausbauen.
Wenn Du aber eher spezifische Anforderungen hast und eine komplette FileSystem-Abstraktion eigentlich zu viel ist, wäre eine spezifische Abstraktion mit genau den Funktionen, die Du so brauchst, vermutlich die klügere Wahl.
Oder Du schaust nach vorhandenen Abstraktionen, z.B. System.IO.Abstractions. Das ist nicht von Microsoft (ich selber kenne es auch nicht), solche Abhängigkeiten sollten also gut überlegt sein.
Niemand lacht dich aus, wir haben alle Mal angefangen.
Pearl ist aber eine Skriptsprache, C# wird kompiliert, da muss zur Compiletime feststehen, auf welche Variable Du zugreifen willst.
Mit anderen Worten: Geht nicht - zumindest nicht einfach so.
Es gibt einen Weg: Reflection
Grob gesagt kannst Du damit die Metadaten von allem (Klassen, Methoden, Variablen, etc.) abrufen und damit dann das alles verwenden, also auch Variablen abrufen.
var field = typeof(MyClass).GetField(res, BindingFlags.NonPublic | BindingFlags.Instance);
var value = (int)field.GetValue(this);
field.SetValue(this, value);
Das funktioniert, wäre aber nicht der beste Weg, da langsam und fehleranfällig.
Beides kann man zwar umgehen/optimieren, aber der Code wird dadurch nicht einfacher und mit der Optimierung hättest Du nichts gewonnen.
Außerdem kann das nicht dynamisch (z.B. aus einer Konfiguration) erweitert werden und deine Beschreibung klingt so, als könnte das durchaus interessant sein.
Stattdessen solltest Du ein Dictionary bemühen:
private readonly Dictionary<string, int> _values = new()
{
// Hardcoded Werte, wenn nötig
["a"] = 1,
["b"] = 2,
["c"] = 3,
};
public int getAmount(string res)
{
if (_values.TryGetValue(res, out var value))
return value;
return 0;
}
public void setAmount(string res, int value)
{
_values[res] = value;
}
Oder die vielen anderen Möglichkeiten vom Dictionary.
Das hätte dann den Charme, dass Du z.B. aus einer Datei alle Arten mit den Werten lesen und in das Dictionary schreiben könntest.
Du könntest es auch noch ausbauen und nicht nur einen Wert je Eintrag speichern, sondern eine Klasse erstellen, die dann mehrere Werte kapselt und von der Du dann je Name eine Instanz dem Dictionary hinzufügst. So kannst Du auch mehrere Werte verwalten, ohne zig Dictionarys zu brauchen.
(Code im Browser geschrieben)
Wenn Regex, dann so:AnfangGleich.*EndeGleich
Ich würde aber nicht mit Regex arbeiten, sondern mit ganz normalen String-Operationen.
Such den Index von "AnfangGleich", addiere die Länge von "AnfangGleich" und Du hast den Start-Index vom Wert dazwischen.
Such den Index von "Ende Gleich" und Du hast den Ende-Index vom Wert dazwischen.
Und auf die gleiche Weise kannst Du auch den Text vor "AnfangGleich" und nach "EndeGleich" suchen.
Mit diesen Indizes und Substring oder Spans kannst Du dir dann deine gewollten Teil-Strings holen.
Das dürfte um einiges performanter werden, besonders wenn Du viele von diesen Texten untersuchen willst.
Für mich klingt das ehrlich gesagt danach, dass Du die .cs-Datei direkt ohne Projekt geöffnet hast.
Das siehst Du dann daran, dass es im Solution Explorer kein Projekt gibt und links über dem Code steht "Miscellaneous Files".
Die Lösung ist simpel: Öffne das Projekt in Visual Studio und nicht die .cs-Datei direkt.
Und wenn Du kein Projekt hast, dann solltest Du dir einfach Mal die Doku von Microsoft anschauen, die erklären sehr gut, wie man ein Projekt erstellt.
In dein neues Projekt kannst Du die .cs-Datei dann einfach rein kopieren, sollte automatisch erkannt werden.
Wann kommen dabei die eingangs genannten Requests (via IIS) ins Spiel?
Es ist eine Web-Anwendung, der Nutzer tut irgendwas und als Folge davon werden Benachrichtigungen (nicht zwingend an den selben Benutzer) versendet.
Hat jeder Benutzer hier seine eigenen Daten od. sind die Daten für viele / alle Benutzer die gleichen?
Sowohl als auch - vermutlich 😄 Tatsächlich weiß ich das nicht sicher, das wird erst noch erarbeitet und hängt nicht nur von uns ab.
Es sollte schon auch vom Worst-Case ausgegangen werden. Oder zumindest von einer 90% Häufigkeit
Daher die 8000 gleichzeitiger Nutzer, das wäre der geschätzte Worst-Case.
Allerdings kostet das bisher schon (zu) viel Zeit, es steht also auch die Frage im Raum, ob es nicht besser wäre, diesen Worst-Case einfach zu akzeptieren, solange es in den meisten Fällen vernünftig läuft und die Anwendung sich nicht komplett aufhängt.
Was heißt "sofort" (siehe vorhin)?
Wohin wird die Antwort geschickt?
Ggf. haben wir hier ein Kommunikationsproblem?
Mit "sofort" meine ich aus Code-Sicht "sofort", also blöd gesagt: In der nächsten Code-Zeile brauche ich das Ergebnis.
Entweder ich warte im Code synchron oder asynchron (mit await), hauptsache ich kann direkt in der nächsten Zeile im selben Request mit der selben Transaktion darauf reagieren. Ich kann die API-Calls aber nicht im Hintergrund ausführen lassen und nach Beendigung den Benutzer informieren, auch wenn es sich für ihn ggf. wie "sofort" anfühlt.
Die Antwort (z.B. eine Id) wird dann in die DB gespeichert, um irgendwann später (Hintergrund-Job) den Status abfragen und darauf reagieren zu können.
Wenn beim Versenden etwas schief ging, dann muss darauf reagiert werden, indem die komplette Transaktion zurückgerollt wird und dem Benutzer ein Fehler angezeigt wird.
Das Status-Abfragen ist dagegen leichter, weil es (vermutlich) nur im Hintergrund-Thread läuft, da können wir dann hoffentlich asynchron arbeiten.
Welche "Arbeit" und wie werden die (bzw. sollen) die Fehlerinfos angeboten?
Was auch immer der Request tun sollte, das kann alles mögliche sein, effektiv alles, was die Anwendung so tut.
Und angeboten werden die Fehlerinfos nur generisch, weil das dann sehr wahrscheinlich ein Programmierfehler oder schlechte Absprache mit Dritten war, also nichts, wofür der Anwender etwas kann bzw. was er beheben kann. Aber das steht auch noch nicht 100% fest, wir bekommen dazu erst noch Infos.
Warum bringst du eine DB ins Spiel?
Das war eine Überlegung, wie wir das Problem umgehen können.
Wenn wir die Nachrichten nicht versenden, sondern erst speichern und dann in einem Hintergrund-Thread asynchron versenden, gäbe es das Problem nicht mehr.
Das funktioniert aber nicht, da der Request, der die Nachricht versendet, nach dem Versenden selber mit dem Ergebnis weiter arbeiten können muss.
Wie ist hier der Zusammenhang mit den Requests?
Es wird wohl kein async Task max. 2 Monate brauchen
"Prozess" ist hier aus Anwendersicht gemeint.
Also wenn Du online z.B. irgendetwas beantrags, dann füllst Du Formulare aus, bestätigst Kram, wirst informiert, etc.
Das alles ist eine Web-Anwendung, das alles passiert also über Web-Requests.
Und der Zeitraum, in dem Du so einen Antrag einreichen darfst, ist fix vorgegeben und dein Zeitfenster sind 2 Monate.
Mit "sync over async" und max. 8000 Requests wird es so od. so Probleme geben, v.a. in Hinsicht "sofort"
Das ist mir/uns bewusst. Im Worst-Case ist's langsam, daran können wir im Moment nichts ändern, hauptsache die Anwendung läuft weiter und "fängt" sich irgendwann wieder, wenn weniger Leute online sind.
Das ist auch ein generelles Thema, da habe ich gar keinen Einfluss drauf.
Was passiert denn nun wenn ein Request zum IIS kommt?
Wird dann das externe API aufgerufen?
Es passiert alles mögliche. Im Request stehen Daten, es werden Daten in der DB aktualisiert, der Code stellt fest, dass Person X eine Nachricht braucht und ruft die API auf. Es kann aber auch ein Hintergrund-Thread sein, der nachts los rennt und Anträge (um beim Beispiel zu bleiben) aktualisiert und wenn irgendetwas aktualisiert wurde, werden Personen informiert, das läuft wieder über die API.
Wenn nun für eine Hack-Lösung ein weiterer Hack eingebaut wird, so wird es wohl auch nicht besser werden.
Natürlich wird das nicht besser, das ist klar.
Aber das Projekt soll eh neu entwickelt werden - dann natürlich alles asynchron und mit Erfahrungen aus den Fehlern
Deshalb hält sich auch der Wille in Grenzen, solche grundlegenden Probleme anzugehen. Wäre es wenig Aufwand, ok, aber das wäre sehr viel Aufwand, also lieber dafür sorgen, dass es keinen Crash und keinen Deadlock gibt und der Rest ist dann halt so.
Und auch die vielen möglichen Fallstricke, wenn sich das Verhalten vom ThreadPool oder dem IIS ändert:
Ja, das ist ein Problem, aber keines, was wir sinnvoll lösen können.
Ich kann das mit in die Überlegungen einbeziehen, ob es besser ist, die Krücke zu bauen, oder ob ich lieber den ganzen API-Client einmal für HttpClient und einmal für WebClient anbiete. Aber da das Projekt eh nicht mehr super lange leben wird, tendiere ich eher zur Krücke, als jetzt noch mehr Zeit da rein zu stecken.
Mit den spärlichen Infos ist es mir schier unmöglich konstruktiv beizutragen. Wie vorhin schon gefragt, beschreib doch die wichtigen Punkte und nicht warum was nicht geändert werden kann
Im Grunde hast Du alles, was ich weiß
Ich glaube viel eher, wir hängen an einem Kommunikationsproblem fest?
Ich kenne das Projekt leider auch nicht ^^
Ich sitze quasi zwischen den Tischen.
Aber so weit ich das verstehe:
Das ganze Thema soll Benachrichtigungen versenden, das können entweder E-Mails sein, oder andere Wege.
Mir geht's dabei um so einen anderen Weg, das läuft das dann über einen Dienst, auf den ich nur bedingt (kaum) Einfluss habe.
Dieser externe Dienst nimmt (über eine Web-API) Daten entgegen, validiert ziemlich viel und teilt mir mit, ob alles ok ist, oder nicht.
Ich bin derjenige, der den intern genutzten C#-Client dafür entwickelt.
Deshalb kann ich auch nicht sicher sagen, wie der Ablauf konkret sein wird: Es ist effektiv alles, überall, wo Benachrichtigungen raus gehen.
Und das ist auch der Grund, warum wir keine grundlegenden Strukturen (sync->async oder SignalR) umbauen können, wir müssten effektiv das ganze Projekt umbauen.
Und deshalb weiß ich auch keine Größenordnungen. Die 8000 sind eine Schätzung, die man mir mitgeteilt hat - in der Realität wahrscheinlich sehr viel weniger, die 8000 wären dann also eher ein absoluter Ausnahmefall und solange das irgendwie trotzdem verarbeitet werden kann, auch wenn es langsam ist, bin ich prinzipiell fein damit.
Die komplexen Validierungen sind auch der Grund, warum ich immer sofort eine Antwort brauche. Die Daten, die da validiert werden, kommen nicht (nur) von uns, wir können sie also nicht einfach korrigieren, wenn was schief ist und einen zweiten Versuch starten. Wir müssen die Arbeit also abbrechen und Fehlerinformationen anbieten können, wenn es ein Problem gibt. Würden wir die Nachrichten erst in die DB schreiben und irgendwann später versenden, hätten wir diese Möglichkeit nicht mehr, oder wir müssten sämtliche Prozesse grundlegend umstrukturieren.
Oder, wenn es um Status-Updates geht, dann brauchen wir natürlich den Status der Benachrichtigungen, um dann darauf reagieren zu können.
Das Zeitfenster von zwei Monaten kommt daher, dass es Prozesse gibt, an die sich die Anwender halten müssen. Ab Datum X wird ein Prozess freigegeben und für zwei Monate können die Anwender dann an diesem Prozess arbeiten (z.B. einen Antrag stellen), danach wird's dann wieder zu gemacht. Es kann also sein, dass sie sofort ab Datum X los laufen, oder erst 6 Wochen später, oder ein Tag vor Deadline.
Die Kommunikation findet über das Netzwerk statt und das ist - nach meinem Wissen - bisher nur mit dem veralteten WebClient möglich und auch da war's ja eher eine Hack, als eine vernünftige Lösung. Die gleiche Code-Basis soll aber auch in anderen modernen Projekten genutzt werden, ich möchte also möglichst vermeiden, mir eine veraltete Technologie ans Bein zu binden, für ein Projekt, was vermutlich eh in ein paar Jahren grundlegend modernisiert wird.
Ich habe also die Wahl:
Mir fällt es schwer, abzuschätzen, wie groß das Risiko bei Option 2 ist, das versuche ich hier herauszufinden.
Deine Klarstellungen zum ThreadPool hat aber eines erreicht: Meine Annahme zur 2. "Deadlock-Art" war falsch, das Risiko fällt also weg.
Dass die generelle Performance bei vielen gleichzeitigen Requests schlecht sein könnte, das ist verkraftbar. Das kommt vermutlich sowieso nicht so oft vor. Die Anwendung hat generell eine schlechte Performance, es gibt allso Mechanismen, die das behandeln ^^
Entscheidend ist nur, dass die Anwendung weiter läuft, auf gar keinen Fall darf sich alles aufhängen.
Mein Ziel mit dieser Frage ist also teils eine brauchbare Lösung für sync->async zu finden (auch für mich für den Lerneffekt) und um einzuschätzen, welche der beiden Optionen besser ist: WebClient vs. async->sync-Krücke.
Aktuell - maßgeblich durch deine Klarstellung zum ThreadPool - tendiere ich dazu, mit Task.Factory.StartNew einen neuen Task auf dem ThreadPool zu erstellen und synchron darauf zu warten. Damit habe ich das Deadlock-Risiko durch ASP.NET umgangen und bzgl. die Befürchtung, dass es ein zweites Deadlock-Risiko gibt, hast Du aus der Welt geschafft.
Also so:
private TResult execute<TResult>(Func<Task<TResult>> executeAsync, CancellationToken cancellationToken)
{
var task = Task.Factory.StartNew(
function: static x => ((Func<Task<TResult>>)x)(),
state: executeAsync,
cancellationToken,
TaskCreationOptions.None, // Wäre hier eine Option sinnvoll? HideScheduler?
TaskScheduler.Default
).Unwrap();
return task.GetAwaiter().GetResult();
}