Laden...
Avatar #avatar-4119.png
Abt myCSharp.de - Team
Principal Software Engineer Dabei seit 20.07.2008 16.835 Beiträge
Benutzerbeschreibung
Als Principal Software Engineer berate und unterstütze in Form von Entwicklungsleistung aktiv Projekte und Unternehmen in Sachen technischer Zukunft mit dem Fokus auf auf .NET und hoch-moderne Web-Anwendungen/-Landschaften in der Cloud. Ich präsentiere die Technologien von Morgen und helfe bei der Analyse der korrekten Strategie und Methodiken für die Umsetzung. Neben meinem Tech-Blog SCHWABENCODE.com bin ich seit 2011 in exekutiver Funktion dieses Forums tätig, schreibe Artikel und halte ab und zu Vorträge in kleineren Runden zum Thema Cloud, Web-Development und IoT. Seit 2015 wurde ich jedes Jahr zum Microsoft MVP für (.NET / Azure) ernannt.

Forenbeiträge von Abt Ingesamt 16.835 Beiträge

27.09.2023 - 16:32 Uhr

Wenn Du schon deutschen Code hast, lass wenigstens die Umlaute weg... is ja schrecklich 😦


Damit das funktioniert muss a) Dein Zeug die Async/Await unterstützten und b) das Zeug auch richtig umgesetzt sein.

Ich seh hier ein async void, was in der Form nur bei Events erlaubt ist. Das sorgt aber dafür, dass der async-Flow gebrochen wird - es gibt kein Task, auf den "gewartet" werden kann. Auch ist das mit dem Feld "Erg" etwas weird...
Zeig die ganze Nutzung.

26.09.2023 - 11:28 Uhr

"dürfen". Es heisst "dürfen". "müssen" würde ausdrücken, was wäre was schlechtes 😉

19.09.2023 - 20:47 Uhr

Zitat von T-Virus

DLL + Netzlaufwerk ist schon fatal falsche Richtung.
Anwendungen oder DLLs sollte man schon aus Sicherheitsgründen nicht über Netzwerklaufwerke laden/einbinden.

Ist leider weit verbreitet, weil bequem. Aber ja, aus Sicherheitsgründen keine schlaue Idee.

Zitat von holzhaus2022

Hier jammert die Hauptanwendung (FileNotFoundException), ich denke weil meine Klassenbibliothek eine extra DLL für das EDM erzeugt und ich nur die normale DLL lade, welche die Klassenbibliothek entspricht.

Du musst halt alles mitgeben, was relevant für das Laden der DLL ist. Fehlt die DLL selbst oder eine Abhängigkeit, dann gibts besagte Exception.
Willst Du das Ziel weiter verfolgen, würde sich evtl. ein "richtiges" Pluginsystem lohnen, das Dir all die Arbeit und Typisierungsfeatures abnimmt.

19.09.2023 - 15:05 Uhr

Zitat von Kriz

Ok, ihr ratet mir also eher von Microservices ab, und empfehlt eher den Schritt zu gehen, das aktuelle Projekt zu optimieren, so dass weitere Features und Services ohne weiteres implementiert werden können?

Natürlich.

Allein die Frage zeigt, dass Du keine Zielgruppe dafür bist. Microservices sind ein deutlich komplexerer Architekturpattern als ein modularer Monolith.
Davon abgesehen, dass allein der organisatorische Faktor extrem auf Erfahrung aufbaut, hast Du eine technische Komplexität, dessen Du Dir offenbar nicht bewusst bist (sonst würdest nicht fragen, was okay ist).
Hast Du 0 Wissen dazu, wirst Du das nicht handlen können. Daher gern nochmal mein Ratschlag: "hast Du einen Monolithen nicht im griff, wirst Du einen Microservice erst recht nicht im Griff haben".
Das ist der Tod vieler Software-Produkte.

Klar, es gibt nicht DIE Architektur, die auf alles passt, hätte nur gedacht, dass Microservices "alles etwas einfacher machen", aber es scheint eher das Gegenteil zu sein.

Zeigt mir irgendwie aber auch, dass Du noch keine einzige Literatur dazu gelesen hast 😃

Egal welche: ne Architektur anzuwenden oder in Frage zu stellen, ohne sich je damit befasst zu haben, ist keine gute Idee.

19.09.2023 - 13:51 Uhr

Sind leider auch wieder so Hype-Bezeichnungen für Click-Baits (damit SEO funktioniert), die fundamentale Wissensträger eher nicht verwenden.

19.09.2023 - 13:06 Uhr

Vorweg: es gibt nicht die richtige Architektur. Es gibt immer mehrere Wege nach Rom.
Dazu verändert sich eine Architektur mit ihren Anforderungen.

Da sich dieeses Projekt langsam zu einem alles könnenden Monolithen entwickelt und in nächster Zeit nun weitere Funktionen hinzu kommen werden, würde ich alles etwas umstrukturieren wollen und den Ansatz der Microservice-Architektur umsetzen.

Das ist sehr wahrscheinlich der beste, schnellste und effizienteste Schritt, das Projekt gegen die Wand zu fahren.
Wer einen Monolithen nicht richtig im Griff hat, bekommt auch eine Microservice-Landschaft nicht in den Griff.

Ich bin großer Fan und Kritiker von Microservices gleichermaßen; und ich sehe sehr viele Firmen scheitern mit diesem Weg. Aktuell betreue ich mehrere Firmen sowohl beim Schritt zu Microservices (und das sind alles Firmen mit ü100 MA/Projekt) und auch Firmen, das wieder zurück zu bauen, weil sie sich übernommen haben und nun um Hilfe flehen.
Microservices haben für ganz gewisse Szenarien enorme Vorteile, kommen in 100% der Fälle aber immer mit Nachteilen, mit hohem technischen und organisatorischem Overhead und immer mit zusätzlichem Aufwand.
Du redest nur von Features: das ist keine Anforderung, die im Vergleich die Vorteile von Microservices sind.

Firmen, die von Microservices und dessen Vorteile wirklich profitieren, sind die wenigsten. Alle haben aber die Nachteile.
In meinem direkten Dunstkreis ist vor gar nicht all zu langer Zeit eine Firma u.a. aufgrund ihrer gescheiterten Fehlentscheidung auf Microservices und Kubernetes zu gehen Pleite gegangen. Mehrere hundert Mitarbeiter wurden arbeitslos und die Firma wurde/wird abgewickelt.
Grund: die Umstellung wurde enorm unterschätzt, die Verantwortlichen haben sowas noch nie gemacht und sich selbst überschätzt, IT-Systeme gingen monatelang nicht, Kunden sind abgesprungen, geschätzte Devs wegen hoher Unzufriedenheit gegangen.
Ich war zwar nicht direkt beteiligt, aber die Firma hatte zwei Beratungshäuser im Haus, die beide das Vorgehen nicht empfohlen haben. Die Firma hatte keine Not oder Gründe, auf eine solche Plattform zu wechseln. Es kam ein neuer CIO ins Haus (über Vitamin B), der meinte "Kubernetes und Microservices sparen Kosten" - was letzten Endes alles gegen die Wand gefahren und mehrere hundert Mitarbeiter eines >30 Jahre alten Unternehmens arbeitslos gemacht hat.
Und von Außen konnte man nur warnen und zuschauen.

Modulare Monolithen (kein zusammen geschusterter Schrott) haben ihre Vorteile; so viele Vorteile, dass Firmen ihre Microservices wieder abreißen (Amazon, Twitter, Twitch, Netflix...). 
Der Approach sollte immer sein: Monolith First.

As I hear stories about teams using a microservices architecture, I've noticed a common pattern.

  1. Almost all the successful microservice stories have started with a monolith that got too big and was broken up
  2. Almost all the cases where I've heard of a system that was built as a microservice system from scratch, it has ended up in serious trouble.

Microservices haben vor allem für riesige Teams mit großen Skalierungsanforderungen Vorteile. Teams mit erfolgreichen Microservices haben eine sehr hohe Professionalität, Team-Struktur und eine nahezu vollständige Automatisierung.
Aufgrund Deiner anderen Themen in der letzten Zeit, kann ich Dich in diese Gruppe nicht einordnen.

Geh nicht den Microservices-Schritt. Du wirst scheitern, wie viele andere.

19.09.2023 - 10:17 Uhr

Zitat von perlfred

Warum gibt es (in LINQ) nicht gleich einen LeftOuterJoin??? (Die Performing Left Outer Join - Zeile ist ja immer die Gleiche)

Ne, ist sie nicht. DefaultIfEmpty gibt Dir eine Vielzahl von Optionen, die mit einem "einfachen" left outer join nicht möglich sind. Das merkst ja allein schon daran, wo Du DefaultIfEmpty überall einsetzen kannst.

Du hast einfach nur den simpelsten der simpelsten Fälle.

18.09.2023 - 15:04 Uhr

Du verwendest DefaultIfEmpty auf eine Liste von (offenbar) Objekten/Entitäten. Entsprechend ist der Default null und nicht 0.
Das solltest Du problemlos auch debuggen können.

18.09.2023 - 13:58 Uhr

Zitat von cprogrammer

Als Assemly existiert eine AgileDotNetRTPro.dll, welche mit dem ursprünglichen Projekt nix zu tun hat.

Die letzte static Methode wurde hinzugefügt.

Und das verstehst, wenn Du Dir einfach 5 Min das Zeug des Herstellers durchliest: das ist ein durch den Obfuscator erstellter Code-Container, damit Du nicht an den originalen Source Code kommst.

Da Du hier offenbar versuchst ein kommerzielles Produkt zu Reverse-Engineeren, was in der EU verboten ist, und nicht nur für Dich sondern leider auch für myCSharp unschöne Folgen haben kann, mache ich hier dicht.

14.09.2023 - 20:43 Uhr

Nicht demotivierend, sondern weil Du nach Feedback gefragt hast:
so sieht halt ein unstrukturiertes Projekt aus, wo erstmal losgelegt wurde statt sich vorher nen Plan zu machen 😃

  • Projekt hat keine Struktur und trennt keine Verantwortlichkeiten; es fehlt völlig der Ansatz von wieder-verwendbarem Code.
  • Deine Entitäten sind gleichzeitig Business-Modell und UI-Modell, viel Spaghetti-Code
  • Die Datenschicht (Verantwortlichkeit) fehlt komplett
  • Die Verwendung von asynchronen Methoden ist teilweise abenteuerlich / haben versteckte Fehler, die Du niemals per Debugging finden wirst (zB Race Conditions mit Wait() und Co).
  • Das Exception-Handling widerspricht jeglichen Empfehlungen / Guides (Exception Handling (C# Programming Guide))

Ob das alles für Dich relevant ist, musst selbst einordnen. Das sind nur reine Feststellungen, die ich in nem 5 Min Zeitslot bei einer groben Durchsicht gefunden hab.

Zum ConnectionString: warum das ganze gepuzzle mit der Custom Deserialization per Json? Sieht so aus, dass Du das AppSettings-System von .NET nicht im Ansatz verstanden hast. 
Nimm doch einfach die builder.Configuration Property - da steckt alles drin. Kannst in jedem absoluten Basic Tutorial von MAUI nachlesen. Erster Google Treffer: App Configuration Settings in .NET MAUI (appsettings.json)

Bei dem Config-Code muss ich leider nachfragen: jemals den Debugger benutzt, ob in Deinem Konstrukt überhaupt die richtigen Werte geladen werden, oder vesuchst dauernd zB auf nen Empty-String zu connecten?


Die Idee, dass Du via MAUI direkt auf die DB zugreifst: bleib nicht so gut.
So hättest Du schon nicht beginnen sollen. Das kannst auch nicht so einfach "später" migrieren, weil Dein gesamter Code auf Deiner DB Struktur aufbaut, weil halt die Trennung von Verantwortlichkeiten fehlt.
Resultat daraus ist: soll Deine App auf eine API zugreifen, was der richtige Weg wäre, musst im Endeffekt 95% Deines Codes neu schreiben (wobei es im Summe halt auch nur eine super kleine Code-Base ist, eher ein Mini-Tool).

12.09.2023 - 21:46 Uhr

wie ich ja mittlerweile schon gelernt habe, werden Credentials aus Sicherheitsgründen in den Umgebungsvariablen hinterlegt und im Code ausgelesen.

So kann das falsch verstanden werden und entspricht nicht den Aussagen in Richtiger Umgang mit Configuration.
Credentials, wie jede andere Umgebungskonfiguration, gehören nicht in den Code oder andere Dateien. Umgebungsvariablen sind ein Weg, aber nicht der einzige.
Auch ein Cert kann in der Umgebungsvariable liegen oder in dafür vorgesehene Services (1Password, HSM, Azure Vault...) oder in Betriebssystemfunktionalitäten wie DPAPI unter Windows.

Jeder Store ist aber nur so sicher, wie der Zugriff drauf. Und ein spezieller Service ist besser als Umgebungsvariablen.
Es kommt einfach auf die Umgebung und Anforderungen an.

Daher ja: der Windows Cert Store kann auch der richtige Weg sein: Hauptsache das Ding ist nicht Teil Deines Codes oder Deployments.

12.09.2023 - 12:17 Uhr

Wenn es eine reine lokale Desktop-App ist, dann kann sie sich natürlich mit dem lokalen SQL Server verbinden.
Dann ist es eben aber auch eine Desktop-App und keine hybride App.

Der Fehler 26 ist vermutlich der best dokumentierte SQL Fehler überhaupt und zu 99% ein Konfigurationsfehler: er ist halt nicht erreichbar.
Er ist aus, Netzwerk stimmt nicht, Setup stimmt nicht, Connection String stimmt nicht oder was auch immer. Fazit: SQL Server ist nicht erreichbar.

12.09.2023 - 12:07 Uhr

Klar, weil Du auch lokal Deinen SQL Server hast und Connection String auf lokal zeigt.
Das funktioniert so konzeptionell jedoch bei niemand anderem.

Das Konzept von MAUI Apps / hybriden Apps ist immer: Kommunikation über einen zentralen Service.
Das ist auch die Grundlage jedes Sicherheitskonzepts mit Datenbanken. Eine Datenbank darf "von Außen" nicht erreichbar sein.

12.09.2023 - 11:39 Uhr

Was ist Dein Ziel? Dass die Client App sich direkt mit dem SQL Server verbindet? Oder ist das eine Server-Side App?
Wird nicht klar, weil Du von Hybrid sprichst.

Vorweg:
Wenn das durch den Client erfolgen soll, so ist das Setup falsch. Nur eine reine Server-Side-App darf sich mit dem SQL Server verbinden.
Eine Client App muss immer den Weg über eine API gehen. Sowohl aus Infrastruktur- wie auch aus Security-Sicht.

Ist es also eine Client App, dann ist die Fehlermeldung korrekt; der kennt schließlich lokal den SQL Server nicht.

08.09.2023 - 09:24 Uhr

IIRC war der Titel davor "Parameter ermitteln" o.ä. - mittlerweile kann man erwarten einen Titel zu wählen, der was aussagt - vor allem wenn man Hilfe möchte.
Darauf hatte ich mich mit dem Titel-Edit bezogen gehabt (vor 2 Tagen). Vielleicht sagt der neue Titel mehr aus.

08.09.2023 - 08:42 Uhr

Zitat von cprogrammer

Wenn ich die Klasse anlege dann wird GetWindow() leider nicht angeboten, siehe Bild.

Entweder Du meinst Google Suche nach "c# getwindow, Du hast die falsche Klasse oder Deine Methode ist einfach nicht public (oder andere Möglichkeiten..).

04.09.2023 - 13:12 Uhr

Wenn man etwas nicht kennt, dann hilft es immer, wenn man sich die Grundlagen aneignet.
Im entsprechenden Grundlagenartikel zu Resources in .NET gibt es eine vollständige Erklärung der Funktionsweise und Code-Beispiele.

31.08.2023 - 10:56 Uhr

Zitat von echdeneth

Habe es leider nicht geschafft zu erläutern was ich benötige.

Zu "Claims" und "Claim Based Authorization" leider kein Tutorial gefunden wo das verständlich erklärt wird.

https://en.wikipedia.org/wiki/Claims-based_identity

Und wenn ich die Google Suche verwende, dann sehe ich sehr viele Treffer mit Tutorials, Erklärungen und Beispielen verschiedenster Identitätsanbietern.

A claim is a statement that one subject, such as a person or organization, makes about itself or another subject. For example, the statement can be about a name, group, buying preference, ethnicity, privilege, association or capability. The subject making the claim or claims is the provider. Claims are packaged into one or more tokens that are then issued by an issuer (provider), commonly known as a security token service (STS). [2]

Zudem hängt dies mit ASP.NET zusammen welches ich nicht verwende.

Nö. Claims sind nur Teil des generischen "Identity Konzepts", völlig egal was Du für ein Framework oder Technologie verwendest.
In ASP.NET Core Identity ist das nur schon alles fertig eingebaut, weil es ein "Identity Standard" ist, der schon fast 20 Jahre existiert.
Mehr dazu unter zB Identity Metasystem Interoperability Version 1.0 OASIS Standard 2009

Claims können alles mögliche sein: Berechtigungen, Profilinformationen, Feature Flags... alles.
Es ist einfach nur eine Entity Relation zwischen Usern/Rollen und Claims.

Hat jemand Rolle A, dann kommen n Claims mit, hat jemand auch/oder Rolle B, dann kommen nochmal n Claims mit.
Claims können aber auch direkt am User hängen (zB eben Profil-Infos wie Twitter Profil, Punkte, Ränge...)

Siehe meine Info oben:

UserRole als Entity
UserRoleClaim als Entity (typischerweise mit Type und Value)
UserRoleClaimAssoications (n:m Tabelle von UserRole und UserRoleClaim).


In allen Fällen wird ein (gefühlt) mehrjähriges .NET Studium und Erfahrung vorrausgesetzt. Wissen für bereits Wissende, nicht Wissensuchende.

Deswegen ist es immer besser, wenn man dazu schreibt, was der Use Case ist.
Dann müssen a) die Helfer nicht raten und b) die Leute, die das .NET Studium abgeschlossen haben, können Dir mit standardisierten Lösungen helfen / Dich darauf hinweisen.

30.08.2023 - 16:51 Uhr

Zitat von echdeneth

Claim Based Authorization und co - sagt mir gar nichts..

Dann fehlen hier Grundlagen. Gerade bei Security ist das dürftig.
Da ist tatsächlich euer Chef in der Verantwortung, dass ihr das Engagement habt, euch (selbst) weiterzubilden.

Claims sind nicht nur Permissions, sondern jede Art von Identity Property, was Du hier offenbar hast.
Solltest also nun erstmal Zeit nehmen Dich hier einzulesen, statt selbst was (schlechteres) zusammen zu puzzlen...

Das würde dann auch quasi unter "Clean Code" ⇒ Standards fallen.

Ja, die UserRoles zielen darauf ab ein Login und daraufhin die UserRoles abzufragen (ist n JSON-Array)

User Roles sind im Code immer unbekannt. Im Code sind nur Claims bekannt.
Claims können Permissions sein, oder auch einfache Mechanismen. Selbst Profilinfos wie LinkedIn Url, Twitter und Co sind einfach nur Claims mit eigenen ClaimTypes.

Unnötig das selbst zu bauen, wenn es dafür Standards und Best Practises gibt.

Ob da mehr an Sicherheit reinkommt ist Chefsache...

Nein, das ist wiederum Deine Aufgabe. Sowohl die Recherche wie auch die Umsetzung.
Du verantwortest das, nicht Dein Chef. Deswegen bist Du der Dev, nicht Dein Chef.

30.08.2023 - 15:26 Uhr

Kurze Frage nach dem Use Case: willst Du hier eine eigene Art von Claim Based Authorization umsetzen; also Permissions einer Rolle?
Wenn ja, was ich da zumindest raus lese, dann solltest Du mit Entity Relations arbeiten und mit dem entsprechenden Verhalten von Claims. Bei Security eigene Dinge zu erfinden ist meistens keine gute Idee. Behalte standards bei, dann ist das auch einfach zu verstehen, wenn mal neue Leute ins Projekt kommen oder Dinge erklärt werden sollen.

Das heisst:

UserRole als Entity
UserRoleClaim als Entity (typischerweise mit Type und Value)
UserRoleClaimAssoications (n:m Tabelle von UserRole und UserRoleClaim).

ClaimTypes sind ein konstanter String-Wert, mit dem dann die Permission in der App abgefragt werden kann.

Selbst wenn es keine Permission Claims sind, so kannst Du jede Art von Anforderungen über ein Custom Claim Namespace relational umsetzen.
Der statische Zugriff im Code erfolgt immer über den konstanten String-Wert.

http://schemas.mycompany.com/myappname/identity/claims/modi.

Beispiele dazu: System.Security.Claims.ClaimTypes Class


Willst Du den Enum-Weg beibehalten, so sagt der Aufbau von

enum ProgramStatusBehavior {Automatik, Manuell, Beides}

Dass das so eigentlich nicht die Idee ist. "Beides" wäre ein klassischer Fall von Flags.

30.08.2023 - 14:40 Uhr

Ich seh den Zusammehang von 3NF oder gar Clean Code hier nicht.

Enums werden prinzipiell auf DB Ebene als Ganzzahl (int, byte) dargestellt, außer wenn man sich für eine Custom Serialization.
Will man dass ein Enum mehrere Werte annehmen kann, muss dies zusätzlich als Flag markiert werden. Aber auch hier bleibt es technisch eine Ganzzahl (im Endeffekt dann ein Power of Two Bitfield: 1, 2, 4, 8, 16...).

Clean Code schreibt hier nichts vor.

Wie erreriche ich das save die Status zugeordnet und abgefragt werden können?
Per ID und 3. Normalform?

Das ist dann kein Enum mehr, sondern eine Entity Relation.

30.08.2023 - 13:05 Uhr

Zitat von AmpelB

Merkwürdiger ist auch noch, dass das Programm problemlos durchläuft, wenn ich es nicht im Visual Studio (Debugger) laufen lasse, sondern die Exe unter dem bin Verzeichnis starte. Wenn dort die Exception auftreten würde, würde mein Programm doch beendet werden. Und wenn die Exception irgendwo abgefangen würde, würde das doch auch im Visual Studio passieren. Dann könnte ich dort das Programm ja weiter laufen lassen.

Das kommt drauf an, wie Du Deine Debugger Exception Settings eingestellt hast.
Du kannst die Debugger Settings durchaus so einstellen, dass jede Exception im Debugging aufpoppt, egal ob sie danach behandelt wird oder nicht.

Manage exceptions with the debugger in Visual Studio

30.08.2023 - 12:55 Uhr

Darauf achten: die Office 365 REST ist bereits abgekündigt (wurde verlängert, bleibt abgekündigt, wird nicht weiter entwickelt, neue Features nicht vorhanden).
Graph API ist hier die Zukunft (das SDK ist aber leider mies).

29.08.2023 - 17:09 Uhr

Zitat von rockxk

Was soll die zu ersetzende DLL genau ansprechen? Neueres Outlook...

Ja, ein neues Outlook oder eine neuere API. Die Methoden sollten weitestgehend gleich bleiben, um die Anwendungslogik nicht überall anpassen zu müssen.

Das ist eine Sackgasse. Die Office 2016 Basis ist die letzte, die überhaupt diese DLL Schnittstellen hat - und schon dort sind sie nicht mehr garantiert und könnten mit jedem Update raus fallen.
Seit Outlook 2022 ist die seit Jahren einzige unterstützte Schnittstelle Addins.

29.08.2023 - 14:37 Uhr

Zitat von inflames2k

Ich würde Linq und Lambda-Ausdrücke jetzt nicht unbedingt als Grundlagen ansehen wollen. Die setzen schon ein wenig Basiswissen über C# und .NET voraus.

Eigentlich ist die einzige Voraussetzung, dass man sich einmal den Guide sowie die Methoden selbst durchliest.
Ich geb Dir aber in einem Punkt recht: mit einem Try-and-Error-Vorgehen und dem aktiven Verweigern den C# Guide zu lesen, sind Lambdas und Linq durchaus eine Hürde. Ist und bleibt ein Konzept.

Ansonsten ist es ein guter Hinweis der Lernkategorisierung, dass die meisten Tutorials und Guides - auch in anderen Sprachen - generell Lambdas und Linq unter den Einsteiger-Basics führen, dass sie auch ein solches sind. Davon sind Spezalitäten eher ausgenommen, was hier aber nicht der Fall ist.

28.08.2023 - 20:26 Uhr

Der Beschreibung - leider hat das der Thread-Ersteller vergessen - könnte das die alte/veraltete C++ API von Outlook (2010?) sein.
Wenn es das ist, dann ist es eh eine Sackgasse, denn seit 15 Jahren sagt Microsoft: nimm die Office Addin Schnittstellen. Auch die .NET APIs sind seit >10 Jahren abgekündigt.

Könnte auch irgendeine andere hacky Outlook Automatisierung sein über OL. Oder eben ein altes Plugin, das eine andere DLL nutzt und wir sehen genau das.
Die Eingriffsmöglichkeit wäre dann aber so oder so nicht gegeben.


Zitat von gfoidl

Beim gezeigten nativen Code wird mit Inline-Assembler direkt gearbeitet, daher bedenke dass dies mit C# / .NET nicht möglich ist.

Das ist mir gar nicht aufgefallen.. 👍

28.08.2023 - 15:38 Uhr

Zitat von rockxk

Gibt es da irgendwo ein Code-Beispiel?

C unmanaged call .net

Ansonsten den Link angeklicken, den Du direkt - genau deswegen - unter der Antwort erhalten hast.
Die 5Min zwischen den Beiträgen zeigen, dass Du das nicht ansatzweise durchgelesen haben kannst 😉

25.08.2023 - 17:13 Uhr

Les Dir Dein Thema durch, stell Dir vor Du hast keinerlei Hintergrundinfos: würdest Du selbst auf den Thread antworten können?  😃

Schaue ich mir Dein Bild an und beachte Dein Kommentar

(selbe Funktion wie 1 nur andere API syntax)

komm zumindest ich zu dem Entschluss: Dein Wunsch ist mit der Realität nicht kombinierbar. Du musst Deinen Code individuell pro Pluginsystem anpassen/integrieren (zumindest die Schnittstelle).

Das heisst, dass man i.d.R. extra Projekte anlegt, in denen man dann den Code in die Plugins integriert.

MyName.MyApp - Dein Code
MyName.MyApp.Tool1.Plugin - Plugintegration Tool 1
MyName.MyApp.Tool2.Plugin - Plugintegration Tool 2

Wenn Du meinen Hinweis aus den anderen Threads, dass Du Dir die Basics zum Aufbau der Projekte/Namespaces anschaust, beachtet hast, dann erkennst diese Struktur ja wieder.

Das ist im Endeffekt die Basis quasi jeder Provider in .NET, Java, Co..

Beispielweise

Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.Sqlite
Microsoft.EntityFrameworkCore.InMemory

Technisch gesehen auch nichts anderes als dass EFCore andere "Tools" integriert.
Ganz wichtige Grundlage in jeder Art von Software Entwicklung: Dein "Kerncode" hat keine Abhängigkeit zu seinen Nutzern.
Daher ist hier als Beispiel der SqlServer auch nicht Teil von Microsoft.EntityFrameworkCore, sondern eben ausgelagert.

Was Du da im Foto zeigst sieht aus, als ob Du es genau anders rum vor hast/hattest.

24.08.2023 - 23:04 Uhr

Nicht alles, was ein Interface ist, ist DI. Interfaces sind auch nicht pauschal immer der beste Weg.

DI hat was mit "logischen Abhängigkeiten" zutun, die notwendig sind, um einen Use Case auszuführen; IList gehört aber eher zur Kategorie Datenstruktur.
Man verwendet bei solchen Datenstruktur i.d.R. die Implementierung - ausser man will bewusst ein Interface aufgrund von Kompatibilität annehmen/anbieten.

Besser ist es sich an die Grundregel zu halten:

Abstraktion nur so weit wie nötig, konkreter Typ so weit wie möglich.

Hier also ein IList zu nehmen, obwohl man als Typ List hat und auch nur dessen Funktionalität nutzt, macht also keinen Sinn ⇒ konkreter Typ besser Weg.
Bei DI brauchst Du zwar technisch kein Interface, aber konzeptionell schon. Daher ist bei einer logischen Abhängigkeit das Interface die Basis bei DI.

Daher nein: Dein Code hat so in der Form nichts im eigentlichen Sinne zutun (der Code macht in Summe (List im Konstruktor etc.. so einfach wenig sinn).
Ja, man kann hier zwar von Injection der Liste sprechen - aber das hier ist schon eine sehr konstruierte, fiktive Darstellung, die mit der realen Welt eher nix zutun hat.

Edit: Cavemans Beispiel is da deutlich besser.

24.08.2023 - 13:50 Uhr

Zitat von ThomasE.

Indexe gibt es generell auf alle Primary-Keys, sonst nicht.

Das halt generell nicht so dolle.. Indexe sollten so gesetzt sein, dass diese unterstützend zur Anfrage wirken.
Ausnahme wäre, wenn Du eine Datenbank hast, die auf Schreiben optimiert ist und nicht auf Lesen.

Wenn Du eine auf Lesen optimierte DB willst, dann solltest Du so eine Problemstellung ohnehin mit indizierten Views umsetzen.
Ansonsten wirst Du sehr wahrscheinlich immer das Problem des Full Index Scans haben und damit immer langsamer werden, je mehr Daten vorhanden sind.

23.08.2023 - 12:25 Uhr

Architekturen und die Anwendung von Design Pattern sind leider vor allem ein Erfahrungsthema.
Was Du als komplex empfindest, finden andere als super simpel - oder umgekehrt. Komplexität hat auch überwiegend damit zutun, das Projekt und den Code zu kennen. Niemand hier im Forum kennt Deinen Code und weiß was Deine plugin.dll ist.

Es gibt verschiedene Bücher, die die wichtigsten Design Pattern erklären - aber es gibt sehr viele Pattern; und auf einen Anwendungsfall passen auch mehrere Pattern.
Das Must Have Book schlechthin ist Design Patterns. Elements of Reusable Object-Oriented Software. von Erich Gamma.

Daher kann man hier keine pauschale Antwort geben: Software wird schnell komplex, und da gibts kein generelles Gegenmittel dagegen, was im Prinzip auch gut so ist.

Auch ist die Wahrheit, dass es nicht "die perfekten Pattern" gibt. Quasi alle Pattern lösen ein gewisses Problem; bringen aber auch Nachteile mit sich.

Was 99% der .NET Devs nicht beherrschen, aber die absolute Grundlage jeder guten Architektur ist, ist der korrekte Umgang mit Namespaces und Projekten.
Hat man den Framework Design Guide nicht im Griff, hilft Dir auch das beste Buch nichts.
Framework design guidelines - Naming guidelines
Framework design guidelines - Names of Namespaces

und Feedback zu Dir, weil ich Deine anderen Themen kennen: es wirkt so, dass Du das ganze Thema .NET Fundamentals nicht durchgelesen hast und nicht anwendest.
Dir wurde bzgl. den Docs ja eh schon das Feedback gegeben, mal nachzulesen, was Du ja dankend ablehnend beantwortet hast. Ohne Lesen wirds aber schwer sowas zu lernen 😃
Konzepte lernt man leider nicht über Try-and-Error kennen.

Nimm Dir also die Zeit, und lern die Fundamentals.

22.08.2023 - 13:23 Uhr

Zitat von Palladin007

Verstehe ich das richtig, dass ein Handler auch andere Handler aufrufen darf, also eine theoretisch beliebig lange Kette von Handlern entstehen kann?

Das ist prinzipiell völlig valide, sowohl aus Sicht des Mediator-Patterns selbst wie auch aus Sicht von MediatR.
Jimmy schreibt in einem Issue, dass man das aufgrund der Übersichtlichkeit zwar vermeiden soll und es ein paar Dinge wie Refaktorierung schwerer macht (das ist wahr). Das gilt aber generell für den Mediator-Pattern: man bekommt Einfachheit, Flexibilität, Effizient und Co nicht umsonst, sondern leider zulasten der Übersichtlichkeit.
Es ist aber eine konzeptionelle Sache, was Du inhaltlich tust - kein generelles "Nein".

Anders sieht es bei MediatR Notifications aus: diese sind in MediatR "besonders" implementiert und sollten keine Requests (Queries/Commands) absenden. Grund ist, dass hier kein isoliertes DI existiert.
Daher haben wir hier im Forum eine eigene Notification Pipeline in MediatR implementiert, mit der das wiederum technisch möglich ist.

In ASP.NET basiert das System im Endeffekt auf Handlern, die aus anderen Handlern aufrufen wurden.
Das ist halt einfach eine Stuktursache. Man muss sich trotzdem an die Grundregeln einer Architektur halten.

Mit MediatR würde also ein RequestHandler eine IMediator-Dependency erhalten und andere Requests absenden.
Was ist, wenn mehrere Handler teilweise die gleiche oder ähnliche Logik haben?
Es wäre einfach, das alles in einen weiteren Handler auszulagern und den aufzurufen, aber ist das der beste Weg?

Duplikate sollten immer vermieden werden. Auslagern ist der richtige Weg.
Ob Du dafür eine extra Handle-Klasse anlegst oder einen Mediator-Handler macht technisch gesehen wenig Unterschied.
Ich verwende beide Varianten, je nachdem.

Alles über MediatR zu machen hat den technischen Vorteil, dass man darauf Behaviors anwenden kann (zB Logging, Time Tracing etc..).
Daher ist das mein bevorzugter Weg. Siehe mein Beispielprojekt: https://github.com/BenjaminAbt/AspNetCoreEnterprisePlatform


Edit: vielleicht ist es einfacher in der Formulierung:

  • Alle Gemeinsamkeiten, die IO-Operationen beinhalten, sind auf alle Fälle in einem gemeinsamen MediatR Handler
  • Alle anderen Gemeinsamkeiten sind überwiegend in simplen Handler-Klassen.
22.08.2023 - 13:08 Uhr

Es heisst Dependency Injection, weil Du Abhängigkeiten extern mitgibst. Machst Du das nicht, ist es kein Dependency Injection.
Du untergräbst die gesamte Idee von DI mit Deinem Singleton-Konstrukt.

Eine Instanz sollte/darf niemals die Kontrolle haben, die Abhängigkeiten erstellt werden.
Das ist einzig und alleine Aufgabe der DI Konfiguration.


In einem anderen Thread(finde ihn gerade nicht) wurde mir nahe gelegt Datenbanken als Singleton anzulegen. Was ist hier denn in dieser Situation anders, dass man es nicht als Singleton machen sollte?

Quelle?

Das ist absoluter Käse und funktioniert technisch auch nicht. 
Wenn Du einen ADO.NET DB Context verwendest, so ist dieser nicht Thread Safe. Du kannst also keinen Singleton für Dein Repository verwenden.

Nur ganz gewisse Datenbankenverbindungen sind Thread-Safe (zB MongoDB). Relationale Datenbanken generell nicht. Machst das bei relationalen DBs fliegt Dir das ganze Ding um die Ohren, sobald zwei Thread zugreifen. Du musst bei Webanwendungen, anders als auf dem Desktop, immer in Multi-Threading denken.
Es funktioniert technisch also 0. Halt Dich an die MS Docs. Die sind hier korrekt.

Repositories als Singleton macht aber auf Desktops auch i.d.R. keinen Sinn. Eine Verbindung kann immer unterbrechen, und muss dann neu erzeugt werden. Und allein wegen des Testens, etc niemals über eine Property.
Wenn man in einem DI System etwas als Singleton registriert, dann über die Registrierung. Nicht über Properties.

Bitte mach in Deinem eigenen Interesse das Grundlagen Tutorial durch, sonst wirds mit dem generellen Verständnis von DI schwer.

Mein Gedankengang war, dass ich so wenig Logik-Code wie möglich in meinen Controllern habe. Also erstelle ich einen PersonService, an den werden die Requests weiter gegeben und dieser ruft dann die entsprechenden Handler auf. Order macht man da (je nach Komplexität) pro Action einen eigenen Handler der dann die Request übergeben bekommt?

Der Gedankengang ist prinzipiell okay, nur verwendet man in modernen Architekturen keine monolithischen Service Klassen wie "PersonService" mehr. Nur noch Handler. Selbst die moderne ASP.NET Minimal API basiert ausschließlich auf Handlern und verzichtet auf monolitische Controller.

Oder würde der Handler bereits im Controller injiziert werden?

Natürlich im Controller. Du musst den Flow einhalten. Aber Du machst das nicht selbst, das macht das DI Framework für Dich.
Instanzen selbst erzeugen = schlecht. Schlecht weil schlecht Testbar, keine Modularisierung etc. Mach nen DI Tutorial durch und Du verstehst es.

Der obige Code ist "zusammen gezimmerter Beispielcode", und kommt so in meinem Projekt nicht vor.

Dann zeig Dein echten Code, denn niemand halt Lust zu "zusammen gezimmertem Beispielcode" Feedback zu geben, wenn es zu stark verfälscht ist. 😃

22.08.2023 - 12:27 Uhr

Beispiel aus dem Forum hier:

Wir haben einen ForumPostAddController - der Use Case ist nur: Forum Post Add.

Dieser bekommt zwei Dinge injiziert:

 public ForumPostAddController(
     IEventDispatcher eventDispatcher,
     IIdentityContextAccessor identityContext,

Wir erstellen diese Dependencies nicht von Hand: das macht alles das DI Framework.

In der Action wird dann ein Command ausgeführt, der von einem Handler verarbeitet wird:

ForumPostAddCommand cmd = new(forum post values)
_engine.Send(cmd);

Der ForumPostAddCommandHandler wird nun von der MediatR Handler Pipeline aufgerufen und bekommt alle Abhängigkeiten injiziert

    public ForumPostAddCommandHandler(
       IForumPostRepository forumPostRepository,
       IForumPostAttachmentsRepository forumPostAttachmentsRepository,
       IForumUserNotificationsRepository forumUserNotificationsRepository,
       IEventDispatcher engine)

Im Handler selbst also die Handler-Pipeline bekannt, sodass man weitere Queries/Commands/Notifications aufrufen kann.

Der Pattern nennt sich CQS/CQRS bzw. einfach nur Mediator-Pattern.

Alle Dependencies müssen aber injiziert werden, sonst funktioniert das gesamte System nicht und Deine Unit Tests sind die Hölle.
Deswegen gibts DI Frameworks, die einem das abnehmen.

22.08.2023 - 12:16 Uhr

Erster Grundregel: Deine ASP.NET Controller / Actions sind Deine Schnittstelle nach Außen.
Du solltest also eigene Modelle nur für diese Außenwelt bereitstellen: API Modelle. Deine Entitäten oder "Business Models" haben in dieser Welt nichts zu suchen.

Code wie IActionResult result = personService.UpdatePerson(person); zeigt, dass die Logik falsch aufgebaut ist.
Die Logik darf die UI Technologie (hier ASP.NET) nicht kennen. Tut sie aber, weil sie hier IActionResult zurück gibt. Das wiederspricht also der Grundregel, dass Schichten von unten nach oben bekannt sein dürfen, aber nicht umgekehrt. Sonst kann man sich das gesamte mehrschichtige Modell sparen.

Zweiter wichtiger Punkt: jeder Request ist isoliert. Du solltest nur die Instanzen zwischen Requests (=Threads) teilen, die dafür konzipiert sind.
Deine Repositories sind das nicht, und daher Deine Logikbausteine ebenfalls nicht. Das muss also alles als Transient registriert werden - statischen Instance Sharing wie PersonRepository.Instanceist ein No-Go. Technisch gesehen funktioniert das auch gar nicht, weil bei einer korrekten Repository Implementierung Du den Context injiziert bekommst und dieser nicht Thread-Safe ist. Du musst also mit individuellen Instanzen arbeiten.

Generell:

Lass sowas wie "PersonService". Das ist das, was innerhalb von kürzester Zeit in riesen, unflexiblen Code-Klassen endet.
Arbeite mit Use Cases wie eben den Handlern, die Du unten angedeutet hast. Du kannst dafür fertige Frameworks (Wie MediatR) nehmen, die Dir paar Sachen zusätzlich geben, oder alles selbst machen und simpel halten.
Handler werden nach Use Cases aufgebaut und haben dann wirklich nur die Dinge injiziert, die Du für diesen Use Case brauchst; bei großen Service-Klassen hast das Problem, dass Du oft Zeugs injizierst, das Du eben nur in ein paar Methoden brauchst, aber nicht in allen ⇒ Aufbau von unnötigen Overhead, sowohl Runtime wie auch beim Testen.

Das wirkt mir aber irgendwie "nicht richtig", das Repository weiter durch zu reichen, "bis man es braucht".

Doch, das funktioniert genau so. Nur reicht man es nicht durch, sondern das macht alles das DI Framework automatisch. Du erstellst es halt von Hand, was nicht Sinn der Sache ist. Aber der Flow ist:

  • Action nutzt Handler
  • Handler nutzt Repository

Damit ein Handler erzeugt werden kann, braucht man das Repository. So funktioniert DI.

Es kommt dann eben auf den Use Case an, ob das Repository nur etwas zurück gibt oder eben der Handler mehr aufgaben hat.

Was in Deinem Code nicht passt ist, dass Deine UpdatePerson Action Logik beinhaltet.
Sie prüft welcher Handler ausgeführt wird: das ist ebenfalls Logik.
Es ist absolut valide (und notwendig) dass Handler andere Handler aufrufen.

Der Handler würde hier nach Use Case aber eher UpdateMalePersoHandler heissen: ein Handler hat immer nur eine einzige Aufgabe.

Da ich im PersonRepository-Konstruktor die Credentials injiziere.

Das ist falsch, und siehst Du auch in jedem Code Sample zu Datenbanken in den Docs. Verbindungen sollen/müssen von mehreren Repository-Instanzen geteilt werden. Daher bekommt es den Context injiziert, und der Context wurde vom DI (mit Credentials) initialisiert.
Hast Du Credentials im Repository, stimmt bereits die ganze Context-Pipeline nicht - dann kann Dein gesamter DAL nicht funktionieren.

22.08.2023 - 10:50 Uhr

Danke, schaue ich mir an.

21.08.2023 - 14:48 Uhr

Zitat von Palladin007

Wozu auch immer sie ein Repository haben wollten, ich hätte gedacht, der Code reicht 😄

Ist generell üblich, dass man ein lauffähiges Beispiel bringen soll.
Steht auch in den Issue Guidelines der meisten Projekte, hier von MAUI.

Why do we ask for a reproducible example?

Depending on your project a codebase can be pretty big. The bigger your project, the more factors that can be of influence on a bug or issue that you might be seeing. In order to be sure that this is something that is happening in .NET MAUI and not something in your code (or a combination of these things), it is super helpful to have a small sample that reproduces the issue. This way we can:

  • better pinpoint the source of the issue;
  • therefore, we can fix the issue faster;
  • and we can make sure that the issue is not a false positive.

It also acts as a double-edged sword. When you take your code apart piece-by-piece in a new project, adding things back one by one, it will become very clear what different factors are at play here and you might discover that the issue might be something in your code. At the very least you will be able to provide a very detailed description of what you're seeing. That helps us get to the cause faster and resolve the issue quicker.

Dann kann man direkt das Ding ziehen, debuggen und sehen woran es liegt und nicht erst Zeit verschwenden in ein Setup, in dem der Error evtl. nicht auftaucht.
Spart Zeit, Frust und Co.

21.08.2023 - 12:52 Uhr

Dann räum erstmal Deinen Code auf, weil so ist das ziemlicher Murks.
Macht den Eindruck, dass Du selbst völlig den Überblick verloren hast.

Zum Beispiel hast Du auch das Problem, dass Du den gesamten Controller eine Basis Route gibst

    [Route("api/[controller]")]

Und dann der Action nochmal den vollen Pfad

[HttpGet("api/[controller]/[action]/{itemId}")]

Endet also in Summe in

/api/Line/api/Line/GetByForeignID/{itemId}

Fakt ist: das kannst alles selbst via Debugger analyisieren, hundert mal besser als wir mit ner Glaskugel. 😉
Schau an, welchen HTTP Error Du im Client bekommst (vermutlich 404) und fix das Problem. Wir können nur raten, welchen Error Du bekommst.
Selbst im Console Window Deiner API siehst Du ja, welche Route aufgerufen wird. Brauchst nichtmal den Debugger für.

21.08.2023 - 11:27 Uhr

Der Code sieht so aus, dass Du Dich mit ASP.NET noch nicht so wirklich eindringlich befasst hast - hol das nach 😃

    [HttpGet("api/[controller]/[action]/{id}")]   // Matches '/LINE/GetByForeignID/{id}'
       async Task<List<LINE>> GetByForeignID(Int16 itemId)

Deine Route matcht den Parameter "id" aber Dein Parameter heisst "itemId". Das Kommentar stimmt übrigens hier auch nicht.
Vermutlich - das kannst Du nur selbst mit dem Debugger ermitteln - wird daher die Route nicht getroffen, die API liefert Dir 404 mit HTML und dann knallts natürlich beim Lesen via Json.

PS: es sind keine Funktionen, sondern Methoden.

PPS: verwende für Deinen HTTP Client direkt Refit. Machst Dir das Leben 100x einfacher.

21.08.2023 - 10:16 Uhr

Nö, Docker unterstützt schon lange Desktop Apps.
Nennt sich Gui-Enabled Docker Containers.

21.08.2023 - 10:12 Uhr

Google erlaubt so ein Vorgehen nicht und erkennt das eigentlich häufig; schaltet dann Captcha-Abfragen davor.

Kann ich mit einem (einfachen) C# Programm doch nicht so tun, als wäre ich ein Browser und den HTML Inhalt bekommen, den ich auch im Browser sehen würde?

Introduction to Microsoft Edge WebView2
Ergebnis-Gleichheit natürlich nicht garantiert. Garantiert Google nirgends.

20.08.2023 - 17:51 Uhr

Zitat von Palladin007

Ich habe auch keine Lösung für das Problem gefunden, ist scheinbar ein Bug in MAUI.

Ein Bug dazu auf GitHub gemeldet, dass man sich kümmern kann?
Find nämlich 0 Issues zu IRootObjectProvider (ausser ein Roadmap Item), was bisschen weird is, wenns wirklich nen Bug sein sollte.

20.08.2023 - 13:18 Uhr

.NET Framework Varianten sind an den Support-Status von Windows gekoppelt.
Supported client operating systems

.NET Framework 2.x und 3.x sind schon sooo alt, dass die schon ewig nicht mehr Teil von Windows sind.
Microsoft .NET Framework Support Dates

Windows 10- und 11-Systeme sowie auf Server 2012 und neuer laufen soll.

Wenn Du keine Wegwerf-Software bauen willst - .NET Framework 3.5 ist über 15 Jahre alt und schon seit Jahren aus dem Support - die Du bald ohnehin auf eine .NET 6+ Runtime migrieren musst, verwende direkt .NET 6 und liefer optional Deine Anwendung mit Runtime aus.
Install .NET on Windows

17.08.2023 - 10:48 Uhr

Zitat von Duesmannr

Also zusammenfassend, würdest du dazu tendieren, die einzelnen Provider zur Verfügung zu stellen?

"Tendiert"? Also tendiert ist mit Abstand nicht ausreichend, wenn Du nochmal meine Beiträge liest 😄

Hast du eine Empfehlung, sowas umzusetzen? Das man sich nicht immer wiederholt.

Kannst quasi nicht wiederholungsfrei umsetzen. Jede API Version hat ihr eigenes Refit-Interface, respektive den eigenen Provider.
Natürlich kannst Dir über Source Code Generatoren sehr viel abnehmen lassen; aber C# ist halt nun mal typisiert ergo musst damit umgehen.

16.08.2023 - 20:25 Uhr

Auch ist die ApiVersion die genutzt wird, änderbar, wie im Code zu sehen.

Finde ich bei Dir auch eher unglücklich.

Task<string> GetAllAsync(string? memberId, string? ownerId, string apiVersion = "7.0")

Wer sagt denn, dass die GetAll-Methode bei allen API Versionen a) die gleichen Parameter hat oder b) den gleichen Rückgabetyp.
Hinzu kommen verschiedene Arten von Auth oder Exceptions. Das ist alles mit so einem Vorgehen nicht handlebar.

Und nich vergessen: man wiederholt sich halt hart, wenn man bei jeder Methode die Version übergeben muss.

Es ist ja nur Feedback von mir: Du kannst das ruhig so tun. Ich hab mit solchen Konstrukten ausschließlich negative Erfahrung gemacht/mitbekommen und auch in 100% der Fälle eine andere Wurzel des Übels gesehen.
Vielleicht ist das bei Dir anders.


Edit weil vergessen:

Dafür will ich Lazy<T> nutzen, wozu es auch gedacht ist.

Das fällt unter "danger of syntactic sugar", weil u.a. enorm die Komplexität der Implementierung verschleiert wird.
Lazy-Properties kommen mit Nachteilen

16.08.2023 - 20:07 Uhr

Zitat von Duesmannr

Darf ich Fragen, was du daran so schlimm findest?

Schaust Du Dir den Graph Aufbau an, so siehst Du, dass dieser riesig ist.
Ich bin also gezwungen Zeug zu instanziieren, das ich für meine Verwendung einer einzelnen Operation nicht benötige. Der Overhead ist riesig.

Dazu noch fürchterlicher, automatisch generierter Code ohne jegliche Möglichkeit von Generics, was zu ständigen Code-Wiederholungen führt. Könnte darüber Bücher schreiben, wie beschissen das Ding umgesetzt ist. Allein die Klasse BaseRequestBuilder sowie dessen Verwendung in ganzen Provider Requests Implementierungen ist eine absolute Zumutung.

Reaktion von Microsoft zu Community-Feedback dazu: "Wir haben eher Feedback erhalten und nehmen es in die weitere Planung auf."
Was das bedeutet, ist auch klar... es wird sich nichts ändern 😃

Und ggfs. andere Beispiele hast für so einen "Client" der deiner Meinung nach, eine gute Struktur besitzt?

Kann ich Dir nun aus dem Stehgreif nichts sagen. In den meisten Fällen vermeide ich jede Art von solchen überladenen "Clients", wie Du es vor hast und implementier es lieber selbst mit Refit: schlank und so wie ich das brauche.
Bin ich schneller, effizienter und hab ich keine Update-Probleme (wie sie ständig mit dem Graph SDK und Breaking Changes der Fall sind). Abstrahiert werden muss das Zeug ohnehin, denn die SDKs von Microsoft liefern i.d.R. keine Interfaces für eine Abstraktion der Sache. Das heisst, dass der Aufwand ohnehin dazu kommt.

Wenn ich seh, was da die Azure DevOps API alles bietet. "Ich" will doch als Dev keinen überladenen Client haben mit Zeugs, das instanziiert wird, das ich nicht brauche; mich nur Leistung kostet.
Wenn ich "Status" brauche, dann instanziier ich auch nur den "AzureDevOpsStatusProvider" - nicht mehr, nicht weniger. Brauche ich zwei oder drei, dann eben die. Aber ich hab nicht einen Fall, wo wir mehr brauchen.

Wie gesagt; vermutlich auch dem geschuldet, dass unsere Logik-Implementierung durchaus als "sehr gut" zu bezeichnen ist, weil sehr modular und sehr schlank: immer Event- bzw. Use Case getrieben.
Ich glaub weiterhin, dass Du vielleicht einen Aufbau hast, der ungünstig ist, und Du daher "viele Provider" gleichzeitig injizieren musst, was in den meisten Fällen eben auf eine ungünstige Vorgehensweise in der Sofware Architektur hinweist (zB riesige Service-Klassen statt Umsetzung nach Use Cases).

Das Vorgehen hat zusätzlich den Vorteil, dass ich einzelne Provider auf eine andere API-Version (respektive API Urls) betreiben kann als andere Provider, zB um in gewissen Fällen neue/alte Features zu nutzen.
Das wäre mit Deinem Vorgehen, in dem es nur einen einzigen Client für alle gibt, nicht (ohne Zusatzaufwand) möglich.

16.08.2023 - 18:56 Uhr

Meine Frage war ja, einen Client zu haben, der die "Provider" als Properties anbietet um nicht jeden Provider manuell holen zu müssen.

Dann reden wir nicht von einem Client im eigentlichen Sinne. Denn den Client (das Refit Interface), den Du hier hast, kann keine Provider kennen - ergo keine Properties anbieten.

um nicht jeden Provider manuell holen zu müssen

Das Du "so viele Provider manuell holen musst" ist für mich ein Anzeichen, dass die Struktur, die "so viele Provider" erfordert, schon unglücklich ist - und die Wurzel der Ursache.

Flow sollte sein:

  • Client (Dein Refit Interface)
  • Provider bekommt Dein Client injiziert
  • Deine Logik bekommt den/die Provider injiziert

Wenn Du Dir ein Konstrukt baust mit

public class AzureDevOpsClient
{
    public AzureDevOpsUserAccountsProvider AzureDevOpsUserAccountsProvider {get;}

    public AzureDevOpsClient(refitClient)
    {
        AzureDevOpsUserAccountsProvider = new(refitClient)
    }

baust Du Dir etwas, mit dem Du die DI vereinfachst; gleichzeitig hast eben

  • etwas sehr schlecht/umständlich testbares (nur umsetzbar mit mehreren Factories)
  • verschleierst Du (glaube ich) das eigentliche Problem, dass Deine Logik "so viele Provider" gleichzeitig brauchst

Ich bin da kein Freund davon (was nichts heissen muss).
Vielleicht bin ich da auch gebrandmarkt vom Microsoft Graph Client: ieht exakt so aus und ist die mit Abstand schlimmste und qualitativ schlechteste Client-Implementierung, die mir je unter gekommen ist.

16.08.2023 - 18:43 Uhr

Oder soll der Entwickler sich die Provider holen anstatt den Client? Das war ja meine Eingangsfrage.

Ja.

Ein Client ist nur dazu dar die externe Schnittstelle zu definieren (Dein Refit Interface).
Ein Provider ist die Abstraktion der Nutzung. Der Provider macht zB das gesamte Client Handling ( Request, Exception...) sonst müsstest Du das ja jedes mal von Hand machen → DRY.

Deine Logik nutzt die Provider, nicht den Client.

16.08.2023 - 17:41 Uhr

Kannst du mir dafür ein Beispiel geben? Ich habe gerade keine Idee wie das umgesetzt werden sollte.

Steht doch oben

public class AzureDevOpsUserAccountsProvider(IAzureDevOpsApiClient client)

Oder was ist Deine Frage.