Laden...

Forenbeiträge von Palladin007 Ingesamt 2.079 Beiträge

25.05.2023 - 11:01 Uhr

Du kannst das Laden der Assembly auch selber implementieren:

https://learn.microsoft.com/en-us/dotnet/api/system.appdomain.assemblyresolve

Das Beispiel ist etwas doof, im Wesentlichen geht's nur um das AssemblyResolve-Event, das aufgerufen wird, wenn er eine Assembly nicht findet. Dort bekommst Du dann den Namen mitgeteilt und kannst die Assembly selber nach deinen eigenen Regeln von einer Datei laden.

24.05.2023 - 21:33 Uhr

Im FAQ Forum, wenn man dort auf "Aktive Themen" klickt, wird "Neueste Themen" markiert, obwohl man auf "Aktive Themen" ist, das bestätigt auch die URL.

24.05.2023 - 15:40 Uhr

Binäre Serialisierung kann kein Native AOT.
Deshalb gibt's ja die EnableUnsafeBinaryFormatterSerialization Property, die ist standardmäßig false 😄

Aber warum überhaupt binär serialisieren?
Das gilt ja auch aus gutem Grund als obsolet.

Besser wäre, Du arbeitest mit JSON (System.Text.Json), dafür gibt's einen Sourcegenerator, mit dem dann gar kein Reflection mehr nötig ist, sodass es auch mit Native AOT funktioniert.
https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/source-generation

Ich mein, am Ende ist JSON auch nur binär, bloß mit UTF8 kodiert.
Außerdem ist es leichter zu parsen und auch für Menschen leichter zu lesen.
Und wenn Du unbedingt ein unleserliches Format haben will, verschlüssle es, oder mach ein ZIP Archiv daraus.

20.05.2023 - 11:09 Uhr

Nicht einfach

Es gibt Frameworks, die Word-, PDF- oder Excel-Dokumente aus Templates erstellen können, such am besten nach "Report".
Für Word hatte ich mein eigenes Framework geschrieben, da wir kein geeignetes kostenloses Framework gefunden hatten.
Du kannst aber auch Excel versuchen, das sieht nicht so schick aus, dafür gibt's aber ein gutes Framework: ClosedXML.Report
PDF Report Frameworks habe ich selber noch nicht genutzt, daher kann ich dazu nichts sagen.

Die Varianten mit Word oder Excel liefern natürlich kein PDF, aber man kann es ausdrucken (Print to PDF) und hat auf diesem Weg trotzdem PDF. Oder Du suchst nach Frameworks, die das machen.

Alternativ kannst Du auch einen ganz anderen Weg gehen und eine HTML Website erstellen + Code (gibt's meine ich auch Frameworks für), der die Website druckt. Aber auch das habe ich selber noch nicht gemacht.

Mir fällt als Framework IronPDF ein, das kann HTML als PDF rendern, selber genutzt habe ich es aber nicht.

20.05.2023 - 00:48 Uhr

Ich hab's jetzt mit zwei zusätzlichen Projekten gelöst, eine je Version.

Irgendwie schade, dass das Alias-Feature nur so halb funktioniert, aber meine Lösung tut's auch.

Zum Beispiel gibts irgendein Grund, wieso das bei Projekten, die XAML-Dateien haben, nicht funktioniert.

Ich habe zwar XAML-Dateien, aber das war bei mir nicht der Grund, zumindest nicht nur.

Ich hatte es auch nochmal mit einer extra DLL versucht, in der ich alle Versionen zusammenfasse (auch Visual Studio 15 und 16) und interessanter Weise hat's mit den Versionen 15, 16 und und einer der beiden 17-Versionen funktioniert, aber nur eine 17er Version, nie beide.

Vielleicht hängt es damit zusammen, dass die DLLs von Version 17.5 und 17.7 die gleiche Versionsnummer haben, obwohl sie sich unterscheiden.

19.05.2023 - 18:19 Uhr

Hi,

ich habe ein Projekt im alten csproj-Format (kein SDK Format) und 4 DLLs in je 2 Versionen.
Ich habe die DLLs daher entsprechend der Version umbenannt und ihnen je einen Alias passend der Version gegeben.
Die DLLs sind auch alle privat, sie werden von "außen" geladen, darum muss ich mich also nicht kümmern.

Das Ergebnis in der csproj (Reference-Items sortiert, damit V17_5 oben steht):

<Reference Include="Microsoft.VisualStudio.ExtensionEngine.V17_5">
  <HintPath>..\..\lib\2022\Microsoft.VisualStudio.ExtensionEngine.V17_5.dll</HintPath>
  <Private>False</Private>
  <Aliases>V17_5</Aliases>
</Reference>
<Reference Include="Microsoft.VisualStudio.ExtensionEngineContract.V17_5">
  <HintPath>..\..\lib\2022\Microsoft.VisualStudio.ExtensionEngineContract.V17_5.dll</HintPath>
  <Private>False</Private>
  <Aliases>V17_5</Aliases>
</Reference>
<Reference Include="Microsoft.VisualStudio.ExtensionManager.V17_5">
  <HintPath>..\..\lib\2022\Microsoft.VisualStudio.ExtensionManager.V17_5.dll</HintPath>
  <Private>False</Private>
  <Aliases>V17_5</Aliases>
</Reference>
<Reference Include="Microsoft.VisualStudio.ExtensionsExplorer.V17_5">
  <HintPath>..\..\lib\2022\Microsoft.VisualStudio.ExtensionsExplorer.V17_5.dll</HintPath>
  <Private>False</Private>
  <Aliases>V17_5</Aliases>
</Reference>
    
<Reference Include="Microsoft.VisualStudio.ExtensionEngine.V17_7">
  <HintPath>..\..\lib\2022\Microsoft.VisualStudio.ExtensionEngine.V17_7.dll</HintPath>
  <Private>False</Private>
  <Aliases>V17_7</Aliases>
</Reference>
<Reference Include="Microsoft.VisualStudio.ExtensionEngineContract.V17_7">
  <HintPath>..\..\lib\2022\Microsoft.VisualStudio.ExtensionEngineContract.V17_7.dll</HintPath>
  <Private>False</Private>
  <Aliases>V17_7</Aliases>
</Reference>
<Reference Include="Microsoft.VisualStudio.ExtensionManager.V17_7">
  <HintPath>..\..\lib\2022\Microsoft.VisualStudio.ExtensionManager.V17_7.dll</HintPath>
  <Private>False</Private>
  <Aliases>V17_7</Aliases>
</Reference>
<Reference Include="Microsoft.VisualStudio.ExtensionsExplorer.V17_7">
  <HintPath>..\..\lib\2022\Microsoft.VisualStudio.ExtensionsExplorer.V17_7.dll</HintPath>
  <Private>False</Private>
  <Aliases>V17_7</Aliases>
</Reference>

Der Alias V17_7 wird auch erkannt, V17_5 allerdings nicht.

Zuerst (nachdem ich sie hinzugefügt habe) hat er die Referenzen erkannt und im Properties-Fenster auch einen Pfad angezeigt. Nach einem Neuladen des Projektes wurden die Referenzen mit einer Warnung markiert und es wird kein Pfad mehr angezeigt. Gefunden wurde der V17_5 Alias aber nie, egal ob Visual Studio die Referenz angeblich findet, oder nicht.
Wenn ich die Reihenfolge der Reference-Einträge vertauscht (17.7 zuerst), ist das Verhalten genau umgekehrt, er finden V17_5, aber nicht V17_7

Die Warnung, die er anzeigt, ist langweilig:

The referenced component [XYZ.dll] could not be found.

Weiß jemand, woran das liegt und wie ich das beheben kann?

Besten Dank 😃


Es geht um dieses Projekt: https://github.com/madskristensen/ExtensionPackTools

Ich versuche Visual Studio 2022 und die jeweils aktuelle Preview zu unterstützen. Da die referenzierten DLLs sich zwischen den Versionen 17.5 und 17.7 aber unterscheiden, suche ich nach einem Weg, das zu unterscheiden. Der Plan war daher, für beide Versionen den entscheidenden Code, wo der Unterschied relevant sind, zwei mal in je eine Klasse zu schreiben und für die jeweilige Version entsprechend anzupassen. Zur Laufzeit sollte er dann die Version prüfen und nur die richtige Klasse passend zur Version verwenden, zur Not auch mit Reflection, damit der JIT nicht auf die Idee kommt, die falsche Klasse zu kompilieren.

Ob der Plan funktioniert, weiß ich nicht, so weit komme ich ja gar nicht, weil Visual Studio die zweite Version aus irgendeinem Grund nicht findet 😕

Die Alternative wäre, ein viertes Projekt und somit eine vierte Version der Extension anzubieten, allerdings würde ich das gerne vermeiden, dass man auch noch auf die konkrete Version (nicht nur 2022) gucken muss.

16.05.2023 - 13:12 Uhr

Erst einmal danke für das Feedback 😃

Was mir nicht ganz gefällt ist, dass die Prüfung im Build erfolgt.
Das "verschlechtert" meine Developer Experience, weil es länger braucht.

Das stimmt, bei meinen Tests braucht der Analyzer aber nur 42ms?
Der Analyzer lädt auch gar nichtdie aufwändigen Daten, es sollte also immer ungefähr so schnell bleiben.
Der Generator braucht natürlich etwas länger, nachdem die Daten im Cache sind, aber nur wenig.

Wobei mein Test-Projekt aber nur 100 Packages umfässt (XUNit + Moq + Abhängigkeiten + .NET), in großen Projekten, wenn dort dutzende eigene Packages installiert werden, sieht das womöglich weniger rosig aus.

Idee: mach das Ding als .NET Tool, das man als CLI einbinden kann.

Die Idee gefällt mir, werde ich auf jeden Fall umsetzen, aber als .NET Tool und Analyzer, dann kann man es sich aussuchen.
Muss ich mich nur erst einmal einlesen, aber umständlicher als ein Analyzer wird's nicht sein 😄

15.05.2023 - 22:31 Uhr

Guten Abend,

ich möchte ein kleines Projekt von mir vorstellen (und um Reviews bitten):

=== Link entfernt, da ich das Projekt vorerst privat gestellt habe ===

EDIT:
Ich werden das Projekt vorerst ruhen lassen.
Ich habe mir nach Abts Feedback alles nochmal durch den Kopf gehen lassen und ich werde das Projekt neu aufrollen, aber mit ein paar anderen Ansätzen.


Dieses Projekt stellt einen Code Generator zur Verfügung, um Informationen über installierte Packages und deren Abhängigkeiten im Code abzurufen. Es ermöglicht das umfassende Abrufen von Informationen über installierte Packages, einschließlich Abhängigkeiten, und generiert Code zum Zugriff auf diese Daten. Mit dem enthaltenen Analyzer kann die Einhaltung von Lizenzen sichergestellt werden.

Darüber hinaus ermöglicht es eine optionale Konfiguration, die Zugriff auf Icons, Lizenztexte, ReadMe-Inhalte, Versionshinweise und andere Informationen bietet. Es bietet die Flexibilität, diese zusätzlichen Ressourcen basierend auf den eigenen Anforderungen einzubeziehen.

Hauptfunktionen:

- Generieren von Code für einfachen Zugriff auf die Daten
- Analysieren und Durchsetzen der Lizenzkonformität
- Anpassbare Konfigurationseinstellungen
- Nahtlose Integration in bestehende Projekte

Beispiel, wie der generierte Code genutzt wird:

var serilog = PackagesProvider.GetPackageById("Serilog");

foreach (var package in PackagesProvider.GetPackages())
    Console.WriteLine($"{package.Id} | {package.Version.Value} | {package.License?.License}");

// Oder mit einer Instanz:

IPackagesProvider instance = new PackagesProvider();

var serilog = instance.GetPackageById("Serilog");

foreach (var package in instance.GetPackages())
    Console.WriteLine($"{package.Id} | {package.Version.Value} | {package.License?.License}");

Beachtet aber, dass nach der Installation oder einem Neustart von Visual Studio der Generator einige Sekunden braucht, um die Daten zu laden - je nach Einstellung. Im Anschluss sollten diese Daten zwischengespeichert werden, bis Visual Studio geschlossen und der Cache verworfen wird.


Ich verfolge mit diesem Thema nicht nur den Zweck einer Projektvorstellung, ich hoffe auch auf Meinungen, Wünsche, Ideen, Fragen und natürlich auch Kritik - vor allem Kritik 😃

Tests gibt es nur sehr wenig, da die pure Lauffähigkeit der Tests der wichtigste Test von allen ist. Ausführlichere Tests für einzelne Funktionen werde ich noch ergänzen.

Das Projekt selber sollte out the box laufen.
Start-Projekt muss "Loop8ack.PackagesProvider.Analyzer" sein, es gibt ein Launch Profile, das sowohl Generator als auch Analyzer inkl. Debugger für das Test-Projekt startet. Mit den fertigen NuGet-Packages (nuget.config mit artifacts-Ordner ist vorhanden) geht es natürlich auch, allerdings ist die Arbeit damit während der Entwicklung ein wenig ... umständlich, um es freundlich auszudrücken 😃

Wenn Ihr etwas ändern und testen wollt, müsst Ihr ggf. Visual Studio neustarten, da es die Binaries nach irgendeiner Regel unter Temp ablegt und dort wiederverwendet, sodass die Änderungen nicht übernommen werden.


Ich hoffe auf Feedback und Kritik und bedanke mich für Eure Zeit 😃

14.05.2023 - 10:34 Uhr

Ja, das klingt schon mehr nach meinem ursprünglichen Vorschlag.

Ich würde es so machen:

var myData = GetMyData();
var myCriteria = new List<ICriteria>();

myCriteria.Add(new Criteria1(1, 2)); // Fill with data from the ui
// Criterion 2 was ignored - do not add

var isValid = ValidateData(myData, myCriteria);

bool ValidateData(IMyData data, IEnumerable<ICriteria> criterias)
{
    foreach (var criteria in criterias)
    {
        if (!criteria.Validate(data))
            return false;
    }

    return true;
}

class Criteria1 : ICriteria
{
    public int Up { get; }
    public int Down { get; }

    public Criteria1(int up, int down)
    {
        Up = up;
        Down = down;
    }

    public bool Validate(IMyData data)
    {
        // validate data
    }
}

class Criteria2 : ICriteria
{
    public int Left { get; }
    public int Right { get; }

    public Criteria2(int left, int right)
    {
        Left = left;
        Right = right;
    }

    public bool Validate(IMyData data)
    {
        // validate data
    }
}

interface ICriteria
{
    bool Validate(IMyData data);
}

Die Liste kannst Du frei nach Laune aus der UI füllen und auch komplexere Prüfungen gehen nicht in einem ewigen if-else-Spaghetticode unter, sondern sind einzeln und übersichtlich in Klassen organisiert.

13.05.2023 - 23:01 Uhr

(Fast) alles ist möglich 😉
Nur können wir ohne gute Erklärung nicht wissen, was dein Ziel ist.

So wie ich es verstanden habe:

var result = true;

foreach (var s in GetMyString())
{
    if (IgnoreString(s))
        continue;
        
    result = result && GetBoolFromString(s);
}

Console.WriteLine(result);

Wie mir ne Klasse dabei helfen soll ist offen gestanden noch unklar.

Wenn dein Vorhaben so ist, wie gerade beschrieben, brauchst Du nicht.

09.05.2023 - 01:55 Uhr

Ich hab gerade einen ziemlich langen Text für ein neues Thema geschrieben und wollte es abschicken, was allerdings mit der Fehlermeldung abgelehnt wurde:

Deine Anfrage enthält ungültigen oder unerlaubten Inhalt.

Beim zurück navigieren war mein Text weg und was ich falsch gemacht habe, weiß ich auch nicht.
Ich bin jetzt ziemlich salzig, aber mein Text kommt nicht mehr zurück, also bringt's nichts, sich darüber aufzuregen.

Aber ich möchte daher nochmal an das Thema erinnern, dass geschriebene Beiträge im Browser zwischengespeichert werden sollten.
Wenn man aus Versehen neu lädt, oder durch einen Fehler zurück navigieren muss, ist nicht alles weg, man kann dort weiter machen, wo man aufgehört hat.

Ich persönlich würde so ein Feature auf Platz 1 meiner meist gewünschten Features schreiben 😃

07.05.2023 - 21:03 Uhr

Erstelle eine Klasse, die einen Wert beschreibt und Funktionen anbietet, die das Verhalten beschreiben.
Wie genau das aussiehen soll, weißt nur Du.

Wenn die Werte sich deutlich unterscheiden, erstelle so viele Klassen, wie Du brauchst und ein Interface mit den Funktionen, das Du dann in jeder Klasse implementierst.

Danach eine List<IMyInterface>, jeder Eintrag ist eine Instanz der jeweiligen Klasse und die kannst Du dann abarbeiten.

06.05.2023 - 01:02 Uhr

Ich versuche es mal etwas detaillierter:

Jede asynchrone Methode ist eine StateMachine und in sich geschlossen, es wird nichts blockiert.

Wenn Du eine asynchrone Methode aufrufst, wird ein neuer Task erstellt, der erste Schritt der StateMachine ausgeführt und der Task zurückgegeben.
Die StateMachine übergibt dann beim ersten await an den Task (bzw. es wird vorher TaskAwaiter abgerufen) die Kontrolle, damit der die StateMachine informiert, wenn der Task beendet ist. "Informiert" heißt dann, dass die MoveNext-Methode der StateMachine aufgerufen wird, der dann den nächsten Schritt ausführt, also den Code nach dem await bis zum nächsten await.
Wenn der Task bereits beendet war, dann wird die StateMachine synchron informiert, die MoveNext-Methode wird also ganz normal aufgerufen.
Wenn der Task nicht beendet ist, wird die StateMachine erst nach dessen Beendigung informiert, hierfür wir die MoveNext-Methode an den ThreadPool, oder an den aktuellen SynchronizationContext (wenn nicht null) übergeben. Einige Frameworks nutzen den SynchronizationContext, um den Ablauf von asynchronem Code steuern zu können, im Fall von WPF bedeutet das, dass der Code nach einem await nicht an den ThreadPool übergeben wird, sondern an den UI-Thread.
Das geht so lange weiter, bis die Methode beendet ist.

Jeder Code zwsichen den awaits ist also ein einzelner Zustand in der StateMachine.
Und weil es viele awaits gibt und - im Fall des ThreadPools - viele MoveNext-Aufrufe an den ThreadPool übergeben werden, hast Du auch nach jedem await einen anderen Thread - vorausgesetzt, es konnte nicht synchron weiter arbeiten.

Man kann eine asynchrone Methode natürlich auch aufrufen, ohne darauf zu warten, in dem Fall wird trotzdem der erste synchrone Code ausgeführt werden, aber danach ist die aufgerufene Methode dem Anschein nach beendet, das Ergebnis ist der Task. Der Trick dabei ist, dass - je nachdem, wie die Methode entwickelt wurde - eine lang dauernde Aufgabe (z.B. Datei-/Netzwerk-/Datenbank-Operationen, etc.) gestartet werden kann und fleißig im Hintergrund arbeitet, während deine Main-Methode ganz normal weiter läuft. Wenn Du dann auf den Task wartest, ist der im besten Fall schon beendet, oder es dauert nur noch die Hälfte der Zeit bis zur Beendigung. So kann man auch mehrere asynchrone Methoden aufrufen, die dann alle im Hintergrund in eigenen Threads ihre Arbeit verrichten.

Deine asynchrone Main-Methode, die die andere asynchrone Methode aufruft, arbeitet exakt genauso.
Es wird eine StateMachine gesteuert, deine asynchrone Methode gibt einen Task zurück und wenn der fertig ist, wird die Main-StateMachine weitergeführt.
Bei komplexen Anwendungen ist es also eine seeehr lange Kette von StateMachines, die jeweils die nächste asynchrone Methode aufrufen, einen TaskAwaiter von dem Task abrufen, der dann nach Beendigung die eigene MoveNext-Methode aufruft und so weiter.

Daneben gibt's noch die Möglichkeit, synchron auf einen Task zu warten (Result und Wait()), dabei läuft alles genauso ab, nur dass der aufrufende Thread solange tatsächlich blockiert wird, bis der Task beendet ist. 
Das solltest Du aber nur dann tun, wenn Du auch weißt, was Du tust. Z.B. bei WPF führt das sehr leicht zu einem DeadLock.

Daneben gibt es noch andere Möglichkeiten asynchron zu arbeiten, z.B. kann man mit einer TaskCompletionSource einen Task erstellen und von irgendwo anders steuern. Oder man implementiert einen eigenen TaskAwaiter, aber das braucht man normalerweise nicht.


Soviel zum technischen Ablauf - Ich habe diese Details gebraucht, um es wirklich zu begreifen.
Du solltest dich aber trotzdem nochmal selber eingraben, sharplab.io hilft dabei sehr.

Der konkrete Ablauf deines Codes ist also wie folgt:

  • Main: Start Program
  • MethodAAsync aufrufen
  • MethodAAsync läuft bis zum ersten await
  • MethodAAsync ist aus Sicht der Main-Methode beendet
  • MethodAAsync nach dem await wird an den ThreadPool übergeben und läuft dort unabhängig weiter
  • Main: 5x synchron auf Task.Delay() warten (hier wird der Thread blockiert), währenddessen läuft der Rest von MethodAAsync auf dem ThreadPool weiter
  • Main: Wait for taskA termination - bis hier lief die Main-Methode komplett synchron ab, ab jetzt ist der erste Schritt der StateMachine beendet
  • MethodAAsync beendet den Rest der Arbeit, solange passiert in Main nichts - aber es wird nicht blockiert
  • Der Task von MethodAAsync ruft MoveNext der Main-StateMachine auf, wo dann der zweite und letzte Schritt der Main-StateMachine abgearbeitet wird
05.05.2023 - 05:24 Uhr

Ich hab's jetzt eingebaut und es macht meinen Code schon deutlich übersichtlicher.
Es hatte nur das gleiche Problem, warum ich Raw String Literals nicht genutzt hatte: Es verfälscht bei mehrzeiligen Strings die Einrückungen.
Ich hab eine Lösung gefunden, nicht schön, aber funktionsfähig und das reicht mir.
Und als T4-Alternative ist es definitiv besser.

Ich werde es erst einmal so übernehmen, allein schon für den übersichtlicheren Code.

Wenn noch ein anderes Framework kennt, bin ich offen für Vorschläge ^^

04.05.2023 - 11:24 Uhr

So auf den ersten Blick sieht das nach einem guten Konzept aus, ich hatte anfangs auch die Raw String Literals genutzt, bin dann aber davon abgewichen, weil das in manchen Fällen problematischen Code produziert hat.
Eventuell geht das damit besser, dann könnte ich wieder die Raw String Literals, die ja unschlagbar übersichtlich sind.

Ist nicht ganz das, was ich mir erhofft habe, aber es könnte meinen bisherigen Code einfacher machen, das ist schon mal besser, als nichts.
Ich teste es auf jeden Fall, wenn das funktioniert und es kein besseres Framework gibt, dann nutze ich das.

Danke für den Tipp 😃

03.05.2023 - 22:16 Uhr

Speichere das Projekt mal nach C:\Net

Bedenke: Da kann es Probleme mit den Berechtigungen geben, man muss ggf. Vollzugriff erlauben.

Lieber irgendwo anders, z.B. ein zweiter Datenträger, ist egal wo, hauptsache der Pfad ist kurz.

03.05.2023 - 22:02 Uhr

Ne, kannte ich noch nicht, hätte mir bei meinen Experimenten damit einiges an Zeit sparen können 😄

Ich hab's gerade auch nochmal getestet und das Ergebnis ist um ein vielfaches größer, komplexer und unleserlicher, als mein manuelles Schreiben. Für eine der Properties, die ich generieren will, müsste ich > 1000 Zeilen SyntaxTree schreiben - das ist mir zu heftig.

Und für VB.NET müsste ich das ganze nochmal machen, zumindest ist die Factory und alles darin C# spezifisch.

Die Seite ist aber ein schönes Projekt, ich hab sie mir mal abgespeichert, wird sicher nochmal nützlich.

03.05.2023 - 21:08 Uhr

Wenn ich das Punkt für Punkt nachstelle, startet alles korrekt.

Hast Du die Problembehandlung gelesen?
https://learn.microsoft.com/de-de/dotnet/maui/troubleshooting?view=net-maui-7.0

Und geh das mal durch:
https://stackoverflow.com/questions/42020845/error-dep0700-registration-of-the-app-failed-on-windows-10-on-a-macbook-dual

Und entferne im Visual Studio Installer den ganzen ".NET Multi-platform App UI development" Workload und installiere es neu, vielleicht ist deine Installation kaputt.

03.05.2023 - 19:53 Uhr

Von welchem Demo-Programm sprichst Du?

03.05.2023 - 13:58 Uhr

Ja kenne ich, passt für meine Anforderungen aber nicht.
Das Ziel ist ein CodeGenerator, der Daten ausliest und in Code gießt, ich brauche also etwas, das ich dynamisch aus C#-Code heraus steuern kann.

03.05.2023 - 04:22 Uhr

Hi,

ich suche ein möglichst nicht zu komplexes Framework, mit dem ich Code in einen Stream/TextWriter schreiben lassen kann.

Inhaltlich brauche ich nicht sehr komplexe Dinge, grob zusammengefasst: (Nested) Klassen, Properties, Methoden und Aufrufe untereinander. Der Code im Property-Getter ist im wesentlichen ein null-Check, ein Objekt-Initializer und ein return. Die Methoden enthalten einmal ein switch und yield return. Aufwändig sind die Objekt-Initializer, da die verschachtelt sind und viele verschiedene Properties mit unterschiedlichen (generierten) Typen setzen.

Ich habe es mit Microsoft.CodeAnalysis.CSharp versucht, allerdings ist das derart komplex, dass ich lieber den guten alten TextWriter nutze 😄
Außerdem kann ich den Ergebnis-SyntaxTree nicht als VB.NET schreiben lassen, womit es für mich keinen Grund mehr gibt, warum ich das nutzen sollte.

Kennt jemand ein Framework, was sowas bietet, .NET Standard 2.0 unterstützt und idealerweise je nach Wahl C# oder VB.NET schreiben kann?
Letzteres (C# vs. VB.NET) ist nice to have, einfach nur damit mein Projekt eine etwas größere Zielgruppe hat, aber nicht notwendig.

Wäre schön, wenn es etwas Geeignetes schon gibt, ansonsten entwickle ich es selbst 😃

Besten Dank und beste Grüße

30.04.2023 - 23:59 Uhr

Ich könnte mir vorstellen, dass es das, was Du suchst, gar nicht gibt.
Vielleicht gibt es die Objektstruktur, die Du suchst, nur für die einfachere Nutzbarkeit, wenn er einen String parst, wird aber eine interne Darstellung verwendet.

Wenn öffentlich angebotene Methoden nicht das liefern, was Du suchst, suchst Du vielleicht das falsche.

Ich hab mir mal angeschaut, was der Debugger bei deinem Halbkreis-Pfad ausspuckt - im Anhang das Ergebnis.
Das ist nicht das gleiche, wie wenn man manuell aufbaut, aber es sieht aus, als könnte man damit trotzdem alles herausfinden, was man möchte - wenn auch nicht ganz so einfach.

30.04.2023 - 16:50 Uhr

mir ist klar, dass ich noch viel lernen muss

Du verstehst nicht - lass WPF erst einmal liegen und arbeite die Grundlagen mit simplen Konsolenprogrammen durch.

Wie gesagt:
Bevor Du WPF und MVVM lernen willst, sollten die Grundlagen fest sitzen.
Das gilt für alle diese Frameworks, WPF (oder Avalonia, UWP, MAUI, ASP.NET Core, etc.) sind sehr komplex, Du tust dir keinen Gefallen, wenn Du vorschnell damit beginnst.

Du kannst ja später dort weitermachen, wo Du aufgehört hast, oder Du setzt dein Projekt als Konsolen-Anwendung um und baust es dann Stück für Stück in Richtung WPF um. So sammelst Du auch gleich ein paar Erfahrungen zum Thema Architektur - schlechte Architektur-Entscheidungen fallen bei solchen Umbauten auf.

29.04.2023 - 22:15 Uhr

Entweder, Du bist noch sehr viel mehr Anfänger, als dir lieb ist, oder Du kennst kein Pattern Matching.

Bevor Du mit WPF und/oder MVVM arbeitest, sollten die Sprachgrundlagen auf jeden Fall sitzen, beides parallel lernen halte ich für unrealistisch.

Und Pattern Matching:

https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/pattern-matching

Oder aus meinem Code:

if (DataContext is not MyViewModel viewModel)
    return;

... wird vom Compiler umgeformt zu ...

MyViewModel viewModel = DataContext as MyViewModel;

if (viewModel is null)
	return;
28.04.2023 - 19:27 Uhr

Es gibt eine PathGeometry.CreateFromGeometry(geometry) Methode - tut die, was Du brauchst?

27.04.2023 - 22:38 Uhr

Kurze Marketplace-Suche:

https://marketplace.visualstudio.com/search?term=Alignment&target=VS&category=All%20categories&cost=free&vsVersion=vs2022&sortBy=Relevance

Allerdings hatte ich bisher noch nie den Bedarf für sowas.
Wenn Du so eine Wall of Code hast, dass das relevant wird, hast Du vermutlich noch andere Probleme 😉

27.04.2023 - 22:33 Uhr

Du sollst im CodeBehind gar nicht das ViewModel instanziieren.
Guck dir meinen Code ganz zu Beginn an, siehst Du da irgendwo ein new myViewModel()?

Du musst dem DataContext auf irgendeine Weise eine ViewModel-Instanz zuweisen.
Meist beginnt das bei MainWindow manuell, danach gibt's viele verschiedene Wege, z.B. klassisches Binding, oder Prism bietet irgendwas eigenes an, aber den DataContext brauchst Du immer.

Ach ja:

[Artikel] MVVM und DataBinding

26.04.2023 - 20:21 Uhr

Was haltet ihr von dieser Lösung?

Nicht viel 😉

Klingt als hätte dieser Freund seine Erfahrungen hauptsächlich mit HTML/CSS und JavaScript gemacht, da habe ich sowas schon das eine oder andere mal gesehen.

Die in den WPF-PasswordBoxen eingegebene Passwörter im Code-Behind auslesen

Soweit so normal, muss man aber nicht "auslesen", dafür gibt's das PasswordChanged-Event, was ich eingangs erwähnt habe.

auf Übereinstimmung prüfen

Soweit auch normal, allerdings gehört das mMn. in's ViewModel.

Passwort verschlüsseln

Unsinnig.
Der Key muss auch irgendwo stehen, für einen Angreifer ist das also quasi Freitext.
Dann einfach den SecureString verwenden, auch der ist nicht sicher, aber immer noch besser, als selber gebastelte "Sicherheit", die keine ist.

das verschlüsselte Passwort in einen unsichtbaren TextBlock auf der WPF-View übergeben

Wozu? Es ist eine objektorientierte Programmiersprache, man kann das Passwort auch mit einer normalen Variable im RAM behalten - wieder ein Fall für's ViewModel. Geht natürlich auch im CodeBehing, sollte aber nicht.

26.04.2023 - 16:19 Uhr

Wir haben nochmal etwas getestet.

Es hat nichts damit zu tun, ob Excel offen ist.
Das Problem tritt immer auf, wenn die Datei nach dem Auswählen bearbeitet wird.

Eine Lösung haben wir noch nicht gefunden, ich zeige in dem Fall einfach nur eine etwas weniger hässliche Meldung an und lösche die Auswahl, damit der Benutzer die Datei erneut auswählen muss.

26.04.2023 - 16:15 Uhr

Du meinst CultureInfo.InvariantCulture 😃

25.04.2023 - 22:42 Uhr
  • Im Bereich für ein Unterforum führt der "Aktive Themen" Link immer zu "Neuste Themen"

Und zur generellen Themen-Liste:

Dort steht links der Name, darunter das Erstell-Datum & Autor und darunter ggf. noch der Bereich.
Rechts steht der letzte Beitrag, Datum, Autor, Beiträge, Klicks.

Mir ist aufgefallen, dass ich beim Lesen immer auf den linken Bereich achte und unbewusst das Erstell-Datum des Thems als das Datum interpretiere, wann zuletzt geschrieben wurde, während mir der rechte Bereich erst im zweiten Moment "auffällt".

Das würde ich umdrehen, sodass der Text "Letzter Beitrag vor X Minuten von XY" links unter dem Titel steht und rechts der Autor + Erstell-Datum des Themas.

25.04.2023 - 21:49 Uhr

Ich habe denke ich eine Lösung gefunden:

Jedes andere Projekt, das referenziert wird, braucht:

<PropertyGroup>
  <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>

Und das Generator-Projekt bekommt:

<Target Name="EmbedReferencedAssemblies" AfterTargets="ResolveAssemblyReferences">
    
  <!-- Collect all dependent assemblies -->
  <ItemGroup>
    <FilesToEmbed Include="..\Project1\bin\$(Configuration)\*.dll" />
    <FilesToEmbed Include="..\Project2\bin\$(Configuration)\*.dll" />
    <FilesToEmbed Include="..\Project3\bin\$(Configuration)\*.dll" />
  </ItemGroup>

  <!-- Remove duplicates by file name -->
  <ItemGroup>
    <FilesToEmbedByName Include="%(FilesToEmbed.FileName)" FilePath="%(Identity)" />
  </ItemGroup>

  <RemoveDuplicates Inputs="@(FilesToEmbedByName)">
    <Output TaskParameter="Filtered" ItemName="FilteredFilesToEmbed"/>
  </RemoveDuplicates>

  <ItemGroup>
    <FilesToEmbed Remove="@(FilesToEmbed)" />
    <FilesToEmbed Include="@(FilteredFilesToEmbed->'%(FilePath)')" />
  </ItemGroup>

  <!-- Add filtered files as embedded resource -->
  <ItemGroup>
    <EmbeddedResource Include="@(FilesToEmbed)">
      <LogicalName>%(FilesToEmbed.DestinationSubDirectory)%(FilesToEmbed.Filename)%(FilesToEmbed.Extension)</LogicalName>
    </EmbeddedResource>
  </ItemGroup>
</Target>

Und im Code:

internal static class EmbeddedAssemblyLoader
{
    private static readonly Assembly _assembly = typeof(EmbeddedAssemblyLoader).Assembly;
    private static readonly Dictionary<string, Assembly?> _assemblyCache = new();

    public static void Init()
    {
        AppDomain.CurrentDomain.AssemblyResolve += OnAssemblyResolve;
    }

    private static Assembly? OnAssemblyResolve(object sender, ResolveEventArgs e)
    {
        var embeddedName = new AssemblyName(e.Name).Name + ".dll";

        if (!_assemblyCache.TryGetValue(embeddedName, out var assembly))
        {
            assembly = LoadEmbeddedAssembly(embeddedName);

            if (assembly is null)
                Debugger.Launch();

            _assemblyCache.Add(embeddedName, assembly);
        }

        return assembly;
    }

    [SuppressMessage("MicrosoftCodeAnalysisCorrectness", "RS1035:Do not use APIs banned for analyzers")]
    private static Assembly? LoadEmbeddedAssembly(string embeddedName)
    {
        var stream = _assembly.GetManifestResourceStream(embeddedName);

        if (stream is null)
            return null;
        var memory = new MemoryStream((int)stream.Length);

        stream.CopyTo(memory);

        return Assembly.Load(memory.ToArray());
    }
}

EmbeddedAssemblyLoader.Init muss dann im statischen Konstruktor des Generators aufgerufen werden.

Ich bin nicht glücklich damit, aber ...

  • es funktioniert sowohl als Package, als auch als normale Projekt-Referenz
  • ich muss nicht den ganzen NuGet-Package-Baum referenzieren, nur um die Assemblies zu includen
  • es kopiert nicht die ganzen anderen DLLs nach "analyzers/dotnet/cs", wo Visual Studio sie ja als Analyzer zu interpretieren versucht.

Zumindest hat es in meinen Tests funktioniert 😃
Das Problem mit dem Cache habe ich aber immer noch, da hilft nur: Cache löschen.

25.04.2023 - 16:00 Uhr

Sowohl lokal, als auch auf einem Test-Server.
In beiden Umgebungen konnten wir es nachstellen.
Ich arbeite gerade nur lokal, weil es einfacher ist.
Die Datei wurde immer vom lokalen Datenträger (also kein Netzlaufwerk oder ähnliches) hochgeladen.

25.04.2023 - 15:55 Uhr

Grundregel für jede Art von File Upload: niemals im Speicher verarbeiten, sondern im Stream.

Hm - alles klar, nehme ich mit, danke für den Hinweis.

ist die Datei größer als der IIS unterstützt bzw. Deine aktuelle Web.Config (aktiv oder passiv) erlaubt?

Die Datei ist 1,22 KB groß, das sollte ja eigentlich kein Problem sein.
Und Hochladen klappt auch, das Problem tritt nur auf, wenn ich vor dem "Laden"-Klick bearbeite und speichere.

Dein HResult ist ein allgemeiner Fehler, kein Fehlercode.

Das hab ich mir schon gedacht - dann muss es bei der Message-Prüfung bleiben 😕

25.04.2023 - 15:44 Uhr

Guten Nachmittag,

ich habe eine InputFile (Server) Anwendung, in der man eine CSV-Datei auswählen und einlesen kann.
Die Anwendung verwendet dafür die InputFile Komponente und liest sie im Hintergrund in einen MemoryStream - da knallt es.

Folgendes zur Reproduktion:

  • Anwendung mit einem IIS starten - wichtig, nur IIS
  • Die Datei auswählt
  • Datei in Excel bearbeiten, speichern, aber Excel nicht beenden
  • Datei hochladen

Dann tritt folgender Fehler auf:

InvalidOperationException

An error occurred while reading the remote stream: NotReadableError: The requested file could not be read, typically due to permission problems that have occurred after a reference to a file was acquired.

HResult: -2146233079
Keine InnerException

Ich kann den Fehler anhand der Meldung (nach "NotReadableError" suchen) gefiltert fangen und eine sprechende Meldung anzeigen?

Ich möchte wissen, womit das zusammenhängt, kann ich ihn ggf. ganz verhindern, sodass die Datei korrekt gelesen werden kann?
Gibt es eine bessere Möglichkeit, den Fehler eindeutig zu identifizieren, z.B. anhand des HResult und nicht nur der Message?

Schön wäre auch, wenn man die Datei nach dem Fehler nochmal hochladen kann, doch das geht scheinbar nicht ohne Workaround.

Danke und beste Grüße 😃

24.04.2023 - 11:11 Uhr

Vorschläge für die Beitrag erstellen Seite:

  • Ein Link zum letzten Beitrag, auf den man antworten möchte
    Ich nutze das ganz gerne, wenn ich beim Schreiben mir nochmal den ursprünglichen Verlauf anschauen möchte.
    Da ist es praktisch, einen Link zurück zu haben, den ich dann in einem neuen Tab öffnen kann.
  • Eine Übersicht der FAQ- und Artikel-Themen.
    Wenn eine Integration im Editor nicht so einfach ist, würde auch eine einfache Liste rechts unter Tipps reichen.
    Ein Klick auf den Eintrag könnte dann den Link oder den ganzen Markdown-Code (ggf. abhängig von der Markdown-Darstellung) kopieren.
  • Eine Einstellung, um den Tipps-Kasten auszublenden, dann hat man mehr Platz 😃
23.04.2023 - 23:09 Uhr

nicht ganz MVVM konforme

Warum?

Ist absolut MVVM konform.
Meine Lösung ist auch MVVM konform.

Problematisch finde ich allerdings die Komplexität, dass man ein NuGet-Package, einen Command und einen Magic-String, nur um keinen CodeBehind nutzen zu müssen.

Dass man keinen CodeBehind nutzen darf, ist ein Irrglaube, es geht viel mehr darum, wofür man CodeBehind nutzt, also Business-Logik vs. UI-Logik.
Hier geht es nur darum, fehlenden DataBinding-Support nachzurüsten, das fällt unter UI-Logik.

23.04.2023 - 21:14 Uhr

Ich erinnere mich, da war ja was ^^

Dann geht's nicht ohne CodeBehind bzw. lohnt sich nicht.

MVVM lohnt sich aber auf jeden Fall, vorausgesetzt, Du ziehst es durch deine gesamte UI durch.

<PasswordBox PasswordChanged="PasswordBox_PasswordChanged"/>
private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
{
    if (sender is not PasswordBox pwBox)
        return;
        
    if (DataContext is not MyViewModel viewModel)
        return;
        
    viewModel.Password = pwBox.Password;
}
22.04.2023 - 21:10 Uhr

"Value" aktzeptiert kein Binding, das muss ein fest im Code stehender Wert sein.

Was Du braucht ist MVVM.
Definiere im ViewModel zwei Properties für die Passwörter und eine bool-Property, die true ist, wenn die Passwörter sich unterscheiden.
Dann kannst Du an die bool-Property binden und für "Value" trägst Du "True" ein.

22.04.2023 - 13:55 Uhr

Regex solltest Du nicht verwenden, geht zwar, wäre aber vermutlich sehr komplex.

Am besten Du iterierst Zeichen für Zeichen über den Pfad-String und interpretierst das, das Format ist an sich ja ziemlich einfach aufgebaut.

Path Markup Syntax

Du kannst ja einfach auf die Command-Buchstaben prüfen und weißt dann jeweils, was darauf folgen muss, z.B. "L" gefolgt von einer Koordinate bestehend aus zwei Zahlen, ggf. durch Komma oder Leerzeichen getrennt.

18.04.2023 - 20:06 Uhr

Ok, scheinbar funktioniert es doch, allerdings muss ich dafür den ganzen Dependency-Tree der verwendeten NuGet-Packages manuell hinzufügen.
Geht das nicht besser? 😕

Und eine Alternative ohne Package gibt's nicht?
Gerade für's Debuggen wäre es schön, eine Projekt-Referenz zu haben.

Und es gibt noch einen Nachteil:

Der Generator wird als Analyzer gefunden und im VisualStudio angezeigt, die ganzen abhängigen DLLs aber auch.

18.04.2023 - 19:43 Uhr

In Fällen bei denen man zum Beispiel vor lauter Klammern und vielleicht | Verknüpfungen ein ! untergehen sieht, habe ich schon das ! in Leerzeichen gesetzt. Dann fällt es besser auf und der Lesefluss kann trotzdem bei "wenn nicht X" bleiben, statt zu einem "wenn X nicht ist" zu werden.

In solchen Fällen lagere ich das lieber in eine (oder mehrere) Variable(n) aus, und breche jede Teil-Bedingung dann um, sodass das "!" wieder am Anfang der Zeile steht.

Solache langen Bedingungen, wo sowas untergeht, mag ich nicht.

18.04.2023 - 18:22 Uhr

Geht mir ähnlich wie dir, Chilic, wobei "Outing für Anfänger" mMn. kein aussagekräftiges Argument ist 😉

Ich mag es auch am liebsten so, wie man es liest, am besten mit passenden Namen.
Wie gut man das lesen kann, hängt dann natürlich auch von den eigenen Preferenzen ab.

Bezüglich Ausrufezeichen:

Wenn jemand eine Sehschwäche hat, verstehe ich es, wenn man lieber == false schreibt, doch ich mag das nicht.
Aber eine Visual Studio Extension, die sowas umformatiert, wäre sicher ein Gedanke (oder Projekt) wert?

18.04.2023 - 10:27 Uhr

Das hatte ich (glaube ich) ausprobiert, hat aber nur so lange funktioniert, bis Visual Studio seinen Cache benutzt. Wann genau das der Fall ist, konnte ich noch nicht herausfinden.

Ich teste es heute Abend aber nochmal und berichte vom Ergebnis.

18.04.2023 - 09:45 Uhr

Vorschlag:

Für Inline-Code den Margin entfernen, der "zerreißt" mMn. die Text-Optik.

Dies ist ein Test mit Inline-Code.

17.04.2023 - 22:34 Uhr

Guten Abend zusammen,

ich arbeite aktuell an einem Source Generator, der wiederum weitere Projekte benötigt, die wiederum NuGet-Pakete benötigen.
Die eigentliche Funktion läuft, nur der Source Generator beschwert sich immer, dass er seine Abhängigkeiten nicht findet.

Das Problem ist, dass Visual Studio die Source Generator DLL unter in einer Art Cache hält, aber eben nur diese DLL, sonst nichts.
Der Ordner ist bei mir: %temp%\VS\AnalyzerAssemblyLoader\2f880f964e7a46af8432e3c5f292e7f1\7
Dort findet er seine Abhängigkeiten natürlich nicht.

Er scheint generell den Cache zu bevorzugen, wenn er einmal angefangen hat, den Cache zu verwenden, hört er damit nicht mehr auf und ich kann ändern was ich will, er bleibt bei seiner Version im Cache. Ich beende dann immer Visual Studio und lösche den Ordner, aber das kann doch nicht richtig sein?

Ich habe mich an folgenden Beispiel-Projekt orientiert:

https://github.com/dotnet/roslyn-sdk/blob/main/samples/CSharp/SourceGenerators/SourceGeneratorSamples/CSharpSourceGeneratorSamples.csproj

(Kein ISourceGenerator, sondern IIncrementalGenerator)

Sieht also so aus:

<PropertyGroup>
  <GetTargetPathDependsOn>$(GetTargetPathDependsOn);GetDependencyTargetPaths</GetTargetPathDependsOn>
</PropertyGroup>

<Target Name="GetDependencyTargetPaths">
  <ItemGroup>
    <TargetPathWithTargetPlatformMoniker Include="..\Project1\bin\$(Configuration)\netstandard2.0\*.dll" IncludeRuntimeDependency="false" />
    <TargetPathWithTargetPlatformMoniker Include="..\Project2\bin\$(Configuration)\netstandard2.0\*.dll" IncludeRuntimeDependency="false" />
    <TargetPathWithTargetPlatformMoniker Include="..\Project3\bin\$(Configuration)\netstandard2.0\*.dll" IncludeRuntimeDependency="false" />
    <TargetPathWithTargetPlatformMoniker Include="$(PkgMicrosoft_Bcl_HashCode)\lib\netstandard2.0\*.dll" IncludeRuntimeDependency="false" />
  </ItemGroup>
</Target>

Hat funktioniert - genau ein mal.

Ich war zwischendurch auch so weit, alles mit ILMerge zusammen zu kleben, was zu sehr schrägen Fehlern geführt hat - Das ILMerge-Package ist ja auch deprecatet.

Die letzte Idee, die ich habe, ist, dass ich alle abhängigen DLLs als EmbeddedResource hinzufüge und dann dann im AppDomain.AssemblyResolve-Event entpacke. Oder nur die NuGet-DLLs als EmbeddedResource und der eigene Code als Link im Projekt?

Aber das muss doch besser/einfacher gehen?
Weiß jemand dazu mehr?

Schon einmal vielen Dank für die Hilfe 😃

14.04.2023 - 08:24 Uhr

Außerdem sollte man den Cursor so gut wie nie aus dem Code heraus setzen müssen.
WPF arbeitet mit Styles und Triggern, da kann man das abhängig von Eigenschaften und Bindings setzen lassen.
Stichwort: MVVM.

13.04.2023 - 23:18 Uhr

Erstell dir doch einfach eine eigene HttpMethod-Instanz?

Aktueller Source:

private static readonly HttpMethod s_patchMethod = new HttpMethod("PATCH", -1);

https://source.dot.net/#System.Net.Http/System/Net/Http/HttpMethod.cs,24

Ansonsten kann ich Refit empfehlen, das nimmt dir viel Arbeit ab und gibt's auch für .NET Standard 2.0, sollte also mit .NET 4.8 laufen.

13.04.2023 - 22:39 Uhr

Hier der Source - hättest Du auch selber raus suchen und posten können 😉

https://github.com/ClassicUO/ClassicUO/blob/main/src/ClassicUO.Client/Game/UI/Controls/Button.cs

https://github.com/ClassicUO/ClassicUO/blob/main/src/ClassicUO.Client/Game/UI/Controls/Control.cs

Und das Rectangle (bin mir nicht sicher, ob das das richtige Repository ist):

https://github.com/MonoGame/MonoGame/blob/develop/MonoGame.Framework/Rectangle.cs

Alles inklusive dem Abruf des_boundsFeldes funktioniert, richtig?
Und Du möchtest danach Werte vomRectangleabrufen und setzen?

  1. ist das ein Struct, die Werte kannst Du zwar ändern, aber es bringt dir nix, weil es kein Referenz-Typ ist.
  2. X,Y,Widthund Heightsind Felder, keine Properties,GetProperty wird also immernullzurückgeben.
  3. Alle anderen Properties haben kein Backing-Field, sie berechnen den Wert direkt,GetFieldwird also immernullzurückgeben.

Du musst denRectangleWert abrufen, einen neuenRectangleWert erstellen und den dann setzen.