Laden...
Avatar #avatar-3521.gif
pollito myCSharp.de - Member
Dipl.-Ing. Ingenieurinformatik 78591 Durchhausen Dabei seit 26.02.2010 328 Beiträge

Forenbeiträge von pollito Ingesamt 328 Beiträge

08.08.2024 - 20:27 Uhr

Ja, danke, ich kenne sie. Mir geht es dabei um diese Position:

Visual Studio Professional-Abonnements (ohne monatliche Azure-Gutschrift)

Darüber hinaus weiter unten zu lesen:

Diese Tabellen dienen nur allgemeinen Informationszwecken als allgemeine Übersicht über Microsoft-Partnervorteilepakete. Die hierin aufgeführten Programminformationen können sich ändern und sollten nicht als Angebot, Bestätigung, Garantie, Verpflichtung oder sonstige Art von Darstellung von Microsoft interpretiert werden. Vollständige Details und Anforderungen sind festgelegt und unterliegen den anwendbaren Programmberatern und Partnervereinbarungen.

Also es kann sich schon das eine oder andere ändern.

08.08.2024 - 12:14 Uhr

Vielen Dank!

Ich habe diesen Thread vor kurzem entdeckt und mich direkt hier eingeklinkt. Wie bereits erwähnt, finde ich leider keine Informationen über den genauen Leistungsumfang der enthaltenen "Visual Studio Professional Subscription". Daher warte ich nun auf den Rückruf vom Microsoft-Support und hoffe auf eine Klärung.

Sollte ich neue Informationen erhalten, werde ich diese hier posten.

08.08.2024 - 11:06 Uhr

Hallo zusammen!

Ich hoffe, hier kann mir jemand mit Informationen zum Action Pack und seinem möglichen Nachfolger weiterhelfen.

Seit vielen Jahren nutze ich die "Visual Studio Professional Subscription" als Teil des Action Packs. Leider wird das Action Pack ab Januar 2025 nicht mehr angeboten. Im Action Pack sind Lizenzen für verschiedene Microsoft-Produkte enthalten, darunter Server- und Client-Betriebssysteme sowie SQL-Server, die ich als Entwickler gerne für Testzwecke nutze.

Jetzt gibt es das Produkt "Partner Success Core", das ebenfalls eine "Visual Studio Professional Subscription" umfasst. Was ich jedoch noch nicht herausfinden konnte, ist, ob diese VS-Subscription auch die Lizenzen für Microsoft-Server- und Client-Betriebssysteme sowie für SQL-Server oder andere bisher enthaltene Software beinhaltet.

Falls dies nicht der Fall sein sollte, welche einigermaßen kostengünstigen Alternativen gibt es, um an diese Ressourcen zu gelangen?

Seit gestern versuche ich, einen verlässlichen Ansprechpartner bei Microsoft zu erreichen. Ich habe bereits eine Supportanfrage eingereicht, aber bisher noch keinen Rückruf erhalten. Daher wollte ich dieses Thema hier zur Diskussion stellen – möglicherweise betrifft es auch andere, die wie ich das Action Pack nutzen.

Vielen Dank im Voraus für eure Unterstützung!

Viele Grüße

René

26.06.2024 - 14:08 Uhr

Hallo T-Virus,

ich glaube, da prallen Galaxien aufeinander 😃

Es tut mir echt leid, dass du dich von den Regeln so stark eingeengt fühlst. Aber hey, in einer Zeit, in der viele Foren gegen KI kämpfen, ist es doch schön, wenn sich noch Leute finden, die sich austauschen möchten. Regeln sind nun mal dazu da, um befolgt zu werden, nicht wahr? Auch wenn es manchmal schwerfällt, sollten wir uns alle bemühen, den "guten deutschen" Weg zu gehen und Regeln nicht als optional anzusehen – wo kämen wir denn hin?! Wer braucht schon Flexibilität und Anpassungsfähigkeit in einer sich ständig wandelnden Welt, wenn wir stattdessen stur auf Regeln pochen können?

Und ja, du hast völlig recht. Die Herausforderungen moderner Technologien machen es vielen Foren nicht leicht. Man sagt ja, dass künstliche Intelligenz auf dem Vormarsch ist, und vielleicht führt das dazu, dass echte menschliche Interaktionen immer seltener werden. Da ist es wirklich erfrischend, wenn sich noch Menschen wie du und ich die Zeit nehmen, sich auszutauschen und Wissen zu teilen.

Also, bitte weiter so! Möge deine Einhaltung der Regeln stets ein leuchtendes Beispiel für uns alle sein. Denn wir wissen ja: Ein Forum ohne Regeln ist wie ein Tag ohne Regen - voller Möglichkeit und Potenzial, aber irgendwie auch viel zu chaotisch.

In diesem Sinne alles Gute,

René

26.06.2024 - 13:11 Uhr

Hallo zusammen,

wie ich sehe, gibt es hier eine kleine, von mir nicht gewollte Diskussion über Crossposting. Lass uns das mal ganz entspannt angehen – vielleicht hilft das in der Zukunft.

Erhöhte Sichtbarkeit und schnellere Lösungen:
Mehr Augen sehen mehr. Wenn ich dieselbe Frage in mehreren Foren stelle, erreiche ich eine breitere Expertise. Das erhöht die Chance auf eine schnelle und passende Antwort. Ist doch effizient, oder?

Wissenstransfer:
Wenn ich irgendwo eine Lösung finde, teile ich sie in anderen Foren. So profitieren alle. Das ist doch der Sinn der Sache, Wissen zu verbreiten (siehe dazu nächsten Punkt).

Transparenz:
Ja, mea culpa, ich hab das Crossposting nicht erwähnt. Normalerweise tue ich das. Vielleicht war es, wie bereits erwähnt, der Arbeitsdruck oder die schiere Lustlosigkeit, mich wie im Kindergarten über Crossposting zu rechtfertigen.

Effizienz:
Mehrere Experten können gleichzeitig an einer Lösung arbeiten. Das spart Zeit und führt oft zu besseren Ergebnissen. Außerdem, wer will schon alle Eier in einen Korb legen?

Respekt:
Natürlich respektiere ich die Hilfe aller Forenmitglieder – ich habe mich immer korrekt und anständig bei jedem Helfenden bedankt. Wenn ich eine Lösung finde, teile ich sie überall, damit alle davon profitieren können.

Übrigens, zum Thema Regeln: Manchmal sind Regeln dafür da, um hinterfragt zu werden, besonders wenn sie die Effizienz und den Wissensaustausch behindern. Aber keine Sorge, ich werde in Zukunft darauf achten, Crossposting explizit zu erwähnen, um Missverständnisse zu vermeiden.

Außerdem: Klar, ich hätte schreiben können, dass ich die Frage auch woanders gestellt habe. Aber ehrlich gesagt, hatte sich jemand früher schon mal darüber mokiert, und ich hatte echt keine Lust auf kindische Diskussionen. In der Regel gebe ich die Lösung bekannt, aber durch den Arbeitsstress ist das leider untergegangen – man ist nur ein Mensch. So führte diese Crossposting-Regel dazu, dass das Forum selbst nichts davon hat. Vielleicht sollte man mit der Zeit gehen und solche antiquierten Regeln überdenken.

Liebe Grüße

René

26.06.2024 - 12:16 Uhr

Zitat von dannoe

Zumindest erwähnen sollte man das Crossposting. Wenn jemand mit ähnlichem Problem dein Forenbeitrag findet, dann kann derjenige zumindest die anderen Crossposts nach weiteren Lösungen/Ideen durchsuchen.

Noch schöner wäre es natürlich, wenn die Lösung am Ende dann auch hier und in allen anderen Crossposts mitgeteilt wird.

Jo, da hast du Recht. Normalerweise tue ich das auch. Ich weiß auch heute nicht mehr, warum ich das nicht tat. Sicher der Arbeitsdruck, wenn ich mich an das Projekt damals erinnere oder keine Lust, nachdem hier mit diesem Thema wie im Kindergarten umgangen wird Cross Posting – ich weiß es einfach nicht mehr.

26.06.2024 - 11:33 Uhr

@wcseller

Du schreibst:

BTW: Ich würde für solche Dinge immer nur EWS verwenden. Man tut sich deutlich leichter, hat mehr Möglichkeiten und ist nicht auf ein installiertes Outlook angewiesen

Das ist klar, aber löst dieses Problem nicht. Sonst hätte ich das nicht geschrieben.

Zitat von pollito

(..) EWS oder PowerShell sollten nicht verwendet werden.

@dannoe
Ich hoffe, ich muss mich nicht schuldig bekennen, einen Cross-Posting begangen zu haben...

Das Problem habe ich anderwärtig gelöst, was zur Aufgabenstellung sogar besser passt.

Danke und buen dia!

René

17.05.2024 - 11:11 Uhr

Nein, die laufen nicht gleichzeitig parallel. Wenn "Securepoint Antivirus Pro" läuft, ist der Defender abgeschaltet. Schalte ich "Securepoint Antivirus Pro" aus, aktiviert sich automatisch der MS-Defender. Genau letzteres war mir passiert im dritten Punkt der Auflistung.

17.05.2024 - 10:53 Uhr

Zitat von Th69

Zitat von pollito

Bei mir lokal kommt diese Meldung nicht.

Verwendest du einen der Microsoft Antivirenprogramme oder hast du die Warnung komplett deaktiviert (in Outlook)? Evtl. mal kurzzeitig das AV deaktivieren und dann testen, ob die Meldung dann auch bei dir erscheint (quasi als Gegentest).

  • Bis vor wenigen Tagen hatte ich den Microsoft-Defender (oder wie der nun zurzeit heißt) aktiviert – keinerlei Meldungen.
  • Seit einer Woche habe ich das Produkt "Securepoint Antivirus Pro" anstelle von Microsoft – keinerlei Meldungen.
  • "Securepoint Antivirus Pro" deaktiviert – keinerlei Meldungen (allerdings hatte sich der MS-Defender wieder eingeschaltet).
  • "Securepoint Antivirus Pro" UND MS-Defender deaktiviert – Meldung erscheint sofort nach dem Programmstart!

Zitat von pollito

Daher die Idee, das Programm digital zu signieren und das Zertifikat in den Zertifikatspeicher "Vertrauenswürdige Herausgeber" kopieren.

Ich denke nicht, daß dies hier beim Zugriff auf Outlook beachtet wird. Aber kannst es ja mal lokal bei dir testen (wenn du herausgefunden hast, wie du auch bei dir die Warnung erscheinen lassen kannst).

Auf jeden Fall beachtet Outlook die Zertifikate vertrauenswürdiger Herausgeber "... > Einstellungen für das Trust Center > Vertrauenswürdige Herausgeber

Aber ehrlich: Es ist mir so lieber, wenn der Kunde Schuld daran ist 😉

Danke, viele Grüße und ein schönes langes Wochenende!

René

17.05.2024 - 09:20 Uhr

Zitat von Th69

Und schau mal, ob du dort dein Programm zur Liste der "Vertrauenswürdige Add-Ins" hinzufügen kannst?

Kann ich leider nicht. Es handelt sich um ein normales WPF-Programm, welches von einem ERP-System aufgerufen wird und auf Outlook zugreift. Daher die Idee, das Programm digital zu signieren und das Zertifikat in den Zertifikatspeicher "Vertrauenswürdige Herausgeber" kopieren.

17.05.2024 - 09:12 Uhr

Zitat von Th69

Ist auf dem PC denn ein Antivirusprogramm installiert und aktiv?

Ja, McAfee, wenn ich das noch richtig weiß. Das System betreut jemand anders.

17.05.2024 - 08:19 Uhr

Kein einziges Mal. Heute Abend habe ich wieder Zugriff auf den PC, der das Programm ausführt. Dann schaue ich mir die Hilfe an. Bei mir lokal kommt diese Meldung nicht.

16.05.2024 - 17:53 Uhr

Gegeben ist ein Programm, welches u. a. Outlook über die COM-Schnittstelle Microsoft.Office.Interop.Outlook fernsteuert.

Das Programm ist soweit fertig und macht auch das, was es tun soll. Allerdings habe ich folgendes Problem: Beim ersten Zugriff auf Outlook  werde ich von ihm mit nachfolgender Meldung ausgebremst:

Ein Programm versucht, auf Ihre
E-Mail-Adressinformationen in Outlook zuzugreifen.
Geschieht dies unerwartet, klicken Sie auf ’Verweigern’. Überprüfen Sie, ob Ihr Antivirusprogramm auf dem neusten Stand ist.
Klicken Sie auf "Hilfe für weitere Informationen zur
E-Mail-Sicherheit und zum Unterdrücken dieser Warnung.
Zugriff gewähren für 10 Minuten

Hilft es, wenn ich das Programm digital signiere und das Zertifikat in den jeweiligen Zertifikatsspeicher kopiere ("Vertrauenswürdige Herausgeber" oder "Vertrauenswürdige Zertifizierungsstellen"?). Wenn das hilft, wie gehe ich vor? Es handelt sich um ein WPF-Programm mit Ziel .NET 8. Da es nur für interne Zwecke ist, würde ich gerne ein selbst signiertes Zertifikat nutzen.

Danke und lG

René

14.05.2024 - 11:24 Uhr

Hallo,

gibt es eine Möglichkeit, E-Mail-aktivierte öffentliche Ordner ausschließlich mit Microsoft.Office.Interop.Outlook zu ermitteln? EWS oder PowerShell sollten nicht verwendet werden. Das Programm soll auf dem Arbeitsplatz laufen und das installierte Outlook nutzen.

Wenn ich einen E-Mail-aktivierten öffentlichen Ordner gefunden habe, sollte ich die sich darin befindlichen E-Mails auflisten.

Bisher scheitere ich daran. Z. B. habe ich folgende Methode:

// Rekursive Methode zum Auflisten der E-Mail-Adressen der E-Mail-aktivierten öffentlichen Ordner
static void ListPublicFolders(Outlook.Folder? folder, string indent)
{
    if (folder != null)
    {
        foreach (object obj in folder.Folders)
        {
            if (obj is Outlook.Folder)
            {
                Outlook.Folder? subFolder = obj as Outlook.Folder;
                
                if (subFolder != null && subFolder.DefaultItemType == Outlook.OlItemType.olMailItem)
                {
                    Outlook.MAPIFolder? parentFolder = subFolder.Parent as Outlook.MAPIFolder;
                    string              parentName   = parentFolder != null ? parentFolder.Name : "Kein Elternordner gefunden";
                    
                    Console.WriteLine($"{indent}- {subFolder.Name}: {parentName}");
                    
                    if (parentFolder != null)
                    {
                        Marshal.ReleaseComObject(parentFolder);
                    }
                }
                
                ListPublicFolders(subFolder, indent + "  ");
                
                if (subFolder != null)
                {
                    Marshal.ReleaseComObject(subFolder);
                }
            }
        }
    }
}

Die Abfrage

if (subFolder != null && subFolder.DefaultItemType == Outlook.OlItemType.olMailItem)

scheitert daran, dasssubFolder.DefaultItemTypeden WertOutlook.OlItemType.olPostItemlliefert, obwohl der öffentliche Ordner als E-Mail-aktivierter Ordner in Exchange eingerichtet wurde.

Hier speziell handelt es sich um Microsoft 365. In der Exchange-Verwaltung habe ich bei der Erstellung explizit den Haken "E-Mail-aktiviert" gesetzt. Daraufhin erhalte ich zwei weitere Menüpunkte: "Delegierung" und "E-Mail-Eigenschaften".  In "E-Mail-Eigenschaften" kann ich einen Alias und einen Anzeigenamen angeben. Voreingestellt ist in beiden Feldern "Bestellungen". Nun erwarte ich, dass der öffentlicher Ordner E-Mail-aktiviert ist und dessen E-Mail-Adressebestellungen@domäne.tldlautet.

Ich verstehe nicht, warum Outlook den Ordner falsch behandelt (ich kann nur Postings erstellen und keine Mails verschicken).

Vielleicht kann mir jemand auf die Sprünge helfen.

Danke und viele Grüße,

René

30.11.2023 - 14:05 Uhr

@Abt

Deine Probleme hätte ich gerne. Wo gibt das Gras zum kaufen? 😃

Sorry, aber jetzt wird es nur noch langweilig! Neben meinem Ingenieurstudium und anderen Qualifikationen bin ich seit 2018 Datenschutzbeauftragter. Meine Lösung (siehe vorigen Post von mir) ist datenschutzrechtlich in Ordnung und abgenommen (nicht von mir, ich darf das in diesem Fall nicht).

30.11.2023 - 14:00 Uhr

Zitat von T-Virus

Ansonsten hattest du übrigens selbst VPN in den Ring geworfen.

Lediglich erwähnt.

Der Ansatz mit SSH führt zu keinem Datenabfluss, wenn er richtig gemacht wird, auch dann nicht, wenn der Rechner gekapert wird. In die Anwendung integriert, SSH nur mit durch Passphrasen gesicherten Zertifikaten, alle Zugangsdaten und Zertifikate verschlüsselt und serverseitig Zugriff von nur erlaubten IP-Adressen. Die Lösung ist auch von einem IT sachkundigen Datenschutzbeauftragten abgenommen worden und damit für mich der Käs' gegessen.

Nochmals: Es hängt von der Anwendung ab und ich weiß nicht, ob die Software vom OP eine Benutzer- und Mehrzugriffsverwaltung hat. Ich musste die Datenbank einer sehr großen 30-Jahre alten mehrbenutzerfähigen zweischichtigen Anwendung (Client + SQL) möglichst sicher ins Internet bringen, damit die Anwender von überall auf der Welt die Clientanwendung nutzen können. Das war billiger als z. B. RDS. Somit gewann man Zeit, um die Anwendung dreischichtig zu gestalten. Und mehr habe ich auch nicht behauptet.

30.11.2023 - 13:09 Uhr

Und das gibt der Gesetzgeber vor? Ich habe von meiner Erfahrung mit SSH geschrieben. Bei den Anwendungen, um es dabei ging, hatte ich das Problem des gemeinsamen Zugriffs grundätzlich nicht. Dem Originalposter habe ich nur mögliche Interimslösungen aufgezeigt – mehr aber nicht. Seine Software und Möglichkeiten kennen ich nicht – er wird aber wohl abwägen können, was für ihn der geeignete Weg ist.

Der OP hat mit keinem einzigen Wort über das grundsätzliche Problem des gleichzeitigen Zugriffs geschrieben – sehr möglich, dass er dieses Problem hat. Bleibt dennoch eine Vermutung und das muss er lösen.

30.11.2023 - 12:54 Uhr

Wer schwätzt noch über VPN? Sage mir, wer verbietet mir eine SSH-Lösung? Quellenangabe?

30.11.2023 - 00:23 Uhr

In meinem ersten Posting:

Aber wie meine Vorredner bereits schrieben, die sauberste Lösung ist eine API-Schicht auf dem Internetserver.

Im zweiten:

Es kommt schon vor, dass man eine zweischichtige Datenbankanwendung ins Internet bringen muss (zumindest die Datenbank) und da hat man den Salat: Es vergeht u. U. viel Zeit, bis die API-Schicht implementiert wurde. Hier können SSH und VPN Zeit verschaffen.

Ich selbst habe dafür nie VPN verwendet, in einigen Projekten aber die SSH-Lösung genau aus dem Grund, den der vorige Anschnitt anreißt.

Ein 30 Jahre altes Projekt kann man nicht eben auf der linken Arschbacke von zwei auf drei Schichten bringen. So kann man sich u. U. Luft verschaffen, Kunde bringt Verständnis dafür und bleibt an der Angel und man selbst muss nicht in Schönheit sterben.

29.11.2023 - 13:58 Uhr

Wie soll eine sichere und verschlüsselte VPN-Verbindung (z. B. mit durch Passphrasen geschützten SSL/TLS-Zertifikaten) gegen die EU-DSGVO verstoßen?

Im Übrigen die Lösung mit SSH sollte ebenfalls zertifikatsbasierend sein.

Es kommt schon vor, dass man eine zweischichtige Datenbankanwendung ins Internet bringen muss (zumindest die Datenbank) und da hat man den Salat: Es vergeht u. U. viel Zeit, bis die API-Schicht implementiert wurde. Hier können SSH und VPN Zeit verschaffen.

29.11.2023 - 13:07 Uhr

Eine nicht ganz schöne aber dennoch praktikable Lösung, wenn man die API-Schicht auf dem Internetserver (noch)nicht machen will: Einen SSH-Tunnel. Man muss aber dafür sorgen, dass der Tunnel immer aufgebaut bleibt – dazu über einen Interceptor prüfen und eventuell Tunnel wieder aufbauen. Das ist eine Möglichkeit. Man könnte den Tunnel auch außerhalb der Anwendung systemweit erstellen, prüfen muss man die Verbindung trotzdem.

Eine andere Möglichkeit ist die Nutzung von VPN.

Aber wie meine Vorredner bereits schrieben, die sauberste Lösung ist eine API-Schicht auf dem Internetserver.

LG

René

22.11.2023 - 11:01 Uhr

Zitat von BlonderHans

Für das reine Anzeigen kannst du PreviewHandler verwenden.

Das Einbetten so eines PreviewHandlers in WPF siehst du in PreviewHost (GeeLaw)

Danke! Das werde ich mir auf jeden Fall anschauen. Es sieht vielversprechend aus.

Aktuell zeige ich die Excel-Dateien in einer separaten Excel-Instanz an. Nicht ganz schön, aber es scheint zu funktionieren. Hier ein ein kleines Testprogrämmchen:

using System.Windows;
using Microsoft.Win32;
using Excel = Microsoft.Office.Interop.Excel;
namespace ExcelInWPF
{
   public partial class MainWindow : Window
   {
       Excel.Application app;
       Excel.Workbook?   currentWorkbook;
       public MainWindow()
       {
           InitializeComponent();
           // Ereignisbehandlungsmethode für das Loaded-Ereignis des Fensters hinzu.
           this.Loaded += OnWindowLoaded;
       }
       private void OnWindowLoaded(object sender, RoutedEventArgs e)
       {
           try
           {
               // Neue Excel-Instanz starten.
               app = new Excel.Application();
           }
           catch (Exception ex)
           {
               MessageBox.Show($"Fehler beim Starten von Excel: {ex.Message}");
           }
       }
       // Eventhandler zum Öffnen der Excel-Datei über das OpenFileDialog
       private void OpenFileButton_Click(object sender, RoutedEventArgs e)
       {
           try
           {
               // Aktuell geöffnete Arbeitsmappe schließen, wenn vorhanden.
               if (currentWorkbook != null)
               {
                   currentWorkbook.Close(false);
                   currentWorkbook = null;
               }
               // Öffne den Datei-Öffnen-Dialog.
               OpenFileDialog openFileDialog = new()
               {
                   Filter = "Excel Dateien|*.xls;*.xlsx;*.xlsm",
                   Title  = "Excel-Datei öffnen"
               };
               if (openFileDialog.ShowDialog() == true)
               {
                   // Öffne die ausgewählte Excel-Datei
                   OpenExcelFile(openFileDialog.FileName);
               }
           }
           catch (Exception ex)
           {
               MessageBox.Show($"Fehler beim Öffnen der Excel-Datei: {ex.Message}");
           }
       }
       private void OpenExcelFile(string filePath)
       {
           // Öffne die Excel-Datei in der neuen Instanz.
           currentWorkbook = app.Workbooks.Open(filePath);
           // Zeige Excel an
           app.Visible = true;
       }
   }
}
<Window x:Class="ExcelInWPF.MainWindow"
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
       Title="Excel in WPF" Height="177" Width="237">
   <Grid>
       <Button Content="Excel-Datei öffnen" Click="OpenFileButton_Click" HorizontalAlignment="Center" VerticalAlignment="Center"/>
   </Grid>
</Window>

Hinweis: Testprogramm, daher kein MVVM-Muster.

Danke und lG

René

22.11.2023 - 10:51 Uhr

Zitat von Alf Ator

Hallo pollito

Es gibt fertige Komponenten zu kaufen, die das können.

Leider sind die Preise relativ hoch für dieses Projekt. Syncfusion z. B. liegt bei 4.740 US$ im Jahr. Für nur das Anzeigen etwas zu viel für mich.

Alternativ in Pdf oder Html konvertieren und anzeigen.

In einem alten Projekt war ich ähnlich vorgegangen: Ich hatte dafür den XPS-Writer verwendet und das erstellte Dokument in einem WebBrowser-Fenster dargestellt. Es hatte funktioniert, wenn auch nicht wirklich schön. Nun gibt es heute standardmäßig den PDF-Drucker in Windows – dieser ist aber bei z. B 13 Arbeitsblättern zu langsam. Dann gibt es auch Probleme mit dem Format, wenn einzelne Blätter zu breit sind und noch schlimmer, wenn die DPI-Angaben der Blätter sich unterscheiden.

Gruss
Alf

Danke und lG

René

22.11.2023 - 01:42 Uhr

Hallo!

Wenn das hier das falsche Forum ist, dann bitte in das richtige verschieben. Danke!

Ich möchte Excel-Dateien mit mehreren Arbeitsblättern in einer WPF-Anwendung Anzeigen.

Es geht nur um eine schnelle Ansicht – kein Drucken, keine  Bearbeitung, keine Umwandlung, sonst rein gar nichts.

Vielleicht sehe ich den Wald vor lauter Bäumen nicht, aber ich finde nichts Vernünftiges.

Was ich wegen des Dokumentaufbaus nicht will, ist das Einlesen der Daten, um diese in einem DataGrid darzustellen – das ist für mich keine Lösung. Das Dokument sollte schon richtig gerendert werden.

Eventuell Excel selbst in die WPF-Anwendung einbetten und über die COM-Schnittstelle fernsteuern? Wie würde man hier am sinnvollsten vorgehen?

Danke und lG

René

09.11.2023 - 10:10 Uhr

Hallo,

nach einer Kundenanfrage überlegen wir, unsere Dokumentenverwaltung auf Revisionssicherheit umzustellen. Wir sind ein kleines Unternehmen mit einem selbst entwickelten ERP-System. Unsere derzeitige Dokumentenverwaltung kann unter anderem alle von uns erstellten Dokumente (Rechnungen, Lieferscheine, Auftragsbestätigungen usw.) automatisch als PDF archivieren. Zudem können auch eingehende Dokumente und andere Unterlagen archiviert werden.

Wir sind gerade dabei, "ZUGFeRD" zu implementieren, da dies in naher Zukunft in Deutschland verpflichtend sein wird. In diesem Zusammenhang fragte ein Kunde, ob wir unsere Archivierungslösung revisionssicher gestalten könnten. Das ist eine interessante Idee.

Für uns wäre es nur dann lohnenswert, wenn die Gesamtkosten für eine Zertifizierung in einem akzeptablen Rahmen liegen – einschließlich der anfallenden Folgekosten.

Hat jemand von euch bereits eine solche Zertifizierung durchlaufen? Könntet ihr uns einen groben Anhaltspunkt zu den Zertifizierungskosten und den Einflussfaktoren darauf geben?

Wir würden uns sehr freuen, wenn ihr hierzu einige Informationen teilen könntet.

Vielen Dank im Voraus und herzliche Grüße,

René

02.11.2023 - 20:04 Uhr

Danke euch beiden. Alles wieder OK.

LG

René

02.11.2023 - 17:54 Uhr

Ja, gleich groß, gleich leer... 🙄

02.11.2023 - 17:35 Uhr

OK, was PDB angeht, habe ich selbst die Lösung. In den Projekteinstellungen lassen sich in der Tat die Einstellungen nicht getrennt setzen, aber in der *.csproj doch:

 <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
   <DebugType>full</DebugType>
 </PropertyGroup>
 <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
   <DebugType>none</DebugType>
 </PropertyGroup>

DebugType auf "none" gesetzt und schon funktioniert es.

Jetzt muss ich mich dem andern Problem widmen (*.dll.config).

02.11.2023 - 17:26 Uhr

Gegeben:
Microsoft Visual Studio Professional 2022 (64-Bit), Version 17.7.6
Windows 11 Pro
WPF-Anwendung

Hallo!

Wenn ich meine WPF-Anwendung in einem lokalen Ordner veröffentliche, sollte nur eine einzelne EXE-Datei erstellt werden. Allerdings mogelt sich eine *.pdb und neuerdings *.config.

*.pdb: Ich veröffentliche selbstverständlich die Release-Version und ich würde gerne darauf verzichten, immer wieder diese Symboldatei löschen zu müssen. Leider kann ich in den Projekteigenschaften nicht getrennt für Debug und Release die Erstellung der Symboldatei beeinflussen: Ändere ich die Einstellung für Release, ist Debug auch gleich mitgeändert.

Ich habe daher das Post-Build-Ereignis del /Q "$(TargetDir)*.pdb" für Release implementiert, dennoch erscheint beim Veröffentlichen die PDB-Datei im Ausgabeordner. In bin\Release ist sie aber nicht vorhanden.

*.dll.config: Bis gestern nicht der Fall und nun erscheint auch diese Datei im Veröffentlichungsordner mit folgendem Inhalt:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
</configuration>

Ich habe aber nichts programmiert, was in eine Konfigurationsdatei müsste. Auch diese Datei brauche ich im Veröffentlichungsordner nicht.

Wie erreiche ich ohne große Verrenkungen, dass diese Dateien nicht im Veröffentlichungsordner erscheinen?

Danke und LG, René

30.10.2023 - 18:49 Uhr

Zitat von dannoe

Ist der recipientim globalen Addressbuch versteckt? (siehe https://stackoverflow.com/a/45980562/1627022)

Du bist mein Held. Vielen Dank! Wie oft habe ich die Einstellungen überprüft und diese jedes Mal übersehen.

Du hast meinen Abend gerettet.

LG René

30.10.2023 - 18:04 Uhr

Gegeben:

  • Konsoleanwendung, .NET 7
  • Microsoft 365, Desktopapplikation von Outlook, Exchange-Online
  • Microsoft.Office.Interop.Outlook
    C:\Program Files (x86)\Microsoft Visual Studio\Shared\Visual Studio Tools for Office\PIA\Office15\Microsoft.Office.Interop.Outlook.dll
    Version 15.0.4420.1017

Hallo,

heute habe ich folgendes Problem: Ich muss aus dem Posteingang eines bestimmten Exchange-Postfaches über Outlook bestimmte E-Mails ermitteln, die Excel-Dateianhänge enthalten – diese sollen an einem bestimmten Ort gespeichert werden. Dazu habe ich das unten aufgeführte Testprogramm erstellt, welches das macht, was es auch soll, bis auf einwr Ausnahme.

Ich habe das Testprogramm mit verschiedenen Postfächern getestet, wobei bei einem davon ich nachfolgenden Fehler beim Ausführen der Zeile

MAPIFolder  inbox = outlookNamespace.GetSharedDefaultFolder(recipient, OlDefaultFolders.olFolderInbox);

erhalte:

Dieser Vorgang konnte wegen eines Registrierungs- oder Installationsproblems nicht ausgeführt werden. Starten Sie Outlook neu, und versuchen Sie es erneut. Falls das Problem weiterhin besteht, installieren Sie Outlook erneut.

Dieses Postfach habe ich auf verschiedenen Rechnern getestet, immer mit demselben Misserfolg.

Ich habe die anderen Postfächer mit dem fehlerverursachenden sowohl client- als auch serverseitig verglichen. Ich konnte dabei keine Unterschiede feststellen: Gleiche Einstellungen (eigentlich alles Standardeinstellungen), gleiche Berechtigungen (auch alles auf Standard), gleicher Server.

Bisher konnte ich keine Spur des Fehlers finden und tappe etwas im Dunkeln.

Ich werde selbstverständlich weiter nach der Ursache suchen, dennoch hoffe ich, dass jemand dieses Verhalten kennt und mir einen Tipp geben kann.

Vielen Dank und liebe Grüße

René

using Microsoft.Office.Interop.Outlook;

namespace SaveAttachments;

class Program
{
    static void Main(string[] args)
    {
        List<MailItem>? Emails;

        try
        {
            Emails = new List<MailItem>();

            // args[0]: Postfach (E-Mail-Adresse)
            LoadEmails(Emails, args[0]);

            // args[1]: Ausgabeordnenr
            ExtractXlsxAttachments(Emails, args[1]);
        }
        catch (System.Exception ex)
        {
            Console.WriteLine(ex.Message);
            Console.WriteLine(ex?.InnerException?.Message ?? string.Empty);
            Console.WriteLine();
            Console.WriteLine("SaveAttachments Postfach Ausgabeordner");
            Console.WriteLine();
            Console.WriteLine("Beispiel:");
            Console.WriteLine("\tSaveAttachments mailbox@example.com C:\\Test");
        }
    }

    static private void LoadEmails(List<MailItem> emails, string emailAddress)
    {
        Application outlookApp       = new Application();
        NameSpace   outlookNamespace = outlookApp.GetNamespace("MAPI");
        Recipient   recipient        = outlookNamespace.CreateRecipient(emailAddress);
        MAPIFolder  inbox            = outlookNamespace.GetSharedDefaultFolder(recipient, OlDefaultFolders.olFolderInbox);

        foreach (object item in inbox.Items)
        {
            if (item is MailItem email && HasXlsxAttachment(email))
            {
                emails.Add(email);
            }
        }
    }

    static private bool HasXlsxAttachment(MailItem email)
    {
        foreach (Attachment attachment in email.Attachments)
        {
            if (attachment.FileName.EndsWith(".xlsx"))
            {
                return true;
            }
        }

        return false;
    }

    static private void ExtractXlsxAttachments(List<MailItem> emails, string targetFolder)
    {
        foreach (var email in emails)
        {
            foreach (Attachment attachment in email.Attachments)
            {
                // Pfad definieren, wohin der Anhang gespeichert werden soll.
                string savePath = Path.Combine(targetFolder, attachment.FileName);

                // Prüfen und ggf. den Dateinamen anpassen, um Überschreibungen zu verhindern
                int counter = 1;
                while (File.Exists(savePath))
                {
                    string   fileNameWithoutExtension = Path.GetFileNameWithoutExtension(attachment.FileName);
                    string   extension                = Path.GetExtension(attachment.FileName);
                    savePath                          = Path.Combine(targetFolder, $"{fileNameWithoutExtension}_{counter}{extension}");
                    counter++;
                }

                // Anhang speichern
                attachment.SaveAsFile(savePath);
                Console.WriteLine($"Anhang gespeichert unter: {savePath}");
            }
        }
    }

}
08.10.2023 - 15:48 Uhr

Heute – ausgeschlafen–  habe ich alles rückgängig gemacht, um die Tests wieder durchzuführen und zu dokumentieren. MitConvert Zero DateTime = True"funktioniert alles, wie erwartet. Ich gehe davon aus, dass ich gestern in meiner "Verzweiflung" etwas übersah und daher nicht merkte, dassConvert Zero DateTime = True der Bringer ist. Ich hoffe, es bleibt auch so...

Vielen Dank und liebe Grüße

René

07.10.2023 - 16:54 Uhr

Welchen Entity-Framework-Datenbankanbieter nutzt du? Ich benutze Pomelo.EntityFrameworkCore.MySql (7.0.0).

Ich bekomme trotztAllow Zero DateTimeundConvert Zero DateTimefolgende Ausnahme:

System.InvalidOperationException
 HResult=0x80131509
 Nachricht = The 'MySqlDateTime' property 'Oxarticle.Oxactivefrom' could not be mapped to the database type 'datetime' because the database provider does not support mapping 'MySqlDateTime' properties to 'datetime' columns. Consider mapping to a different database type or converting the property value to a type supported by the database using a value converter. See https://aka.ms/efcore-docs-value-converters for more information. Alternately, exclude the property from the model using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
06.10.2023 - 18:09 Uhr

Ich habe es probiert, aber anscheinend unterstützt EF Core das Mappen von MySqlDateTime auf eine DateTime-Spalte in MariaDB/MySQL nicht. Ich erhalte folgende Ausnahme:


System.InvalidOperationException
 HResult=0x80131509
 Nachricht = The 'MySqlDateTime' property 'Oxarticle.Oxactivefrom' could not be mapped to the database type 'datetime' because the database provider does not support mapping 'MySqlDateTime' properties to 'datetime' columns. Consider mapping to a different database type or converting the property value to a type supported by the database using a value converter. See https://aka.ms/efcore-docs-value-converters for more information. Alternately, exclude the property from the model using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
 Quelle = Microsoft.EntityFrameworkCore.Relational
 ...

Ich versuche einen anderen Weg, aber langsam gehen mir die Ideen aus.

06.10.2023 - 16:34 Uhr

Danke. Dachte ich auch, aber ich habe bei meinen Recherchen ein paar Male nachgelesen, dass der Wert 0000-00-00 00:00:00 aus MariaDB beim Lesen in .NET zu DateTime.MinValue (gleichbedeutend mitnew DateTime(0001, 01, 01)) konvertiert wird.

Der Datentyp des Datenbankfeldes istMySqlDateTime– in meinem speziellen Fall mit dem ungültigen Datum "0000-00-00" in MariaDB/MySQL: Der ValueConverter sollte dazu dienen, dieses Datum (das in .NET nicht als gültiges DateTime-Objekt interpretiert werden kann) in einen Wert zu konvertieren, den ich in meiner Anwendung verwenden kann (wie z. B. DateTime.MinValue). Ebenso sollte beim Schreiben in die Datenbank DateTime.MinValue wieder in "0000-00-00" konvertiert werden. Das alles gelingt mir aber nicht

Grüße

René

06.10.2023 - 13:44 Uhr

Gegeben:
Datenbank: MariaDB, Version 10.5.19-MariaDB-0+deb11u2
Entity-Framework-Datenbankanbieter: Pomelo.EntityFrameworkCore.MySql (7.0.0)
Ziel-Frameworg: .NET 7.0

Hallo und einen schönen Tag!

Ich muss eine bestehende Datenbank (E-Shop OXID) nutzen, die einige DateTime- und DateOnly-Felder hat, die den Wert "0000-00-00 00:00:00" bzw. "0000-00-00" haben können. Egal, was ich bisher versuche, beim Zugriff auf solche Daten wie z. B.

List<Oxarticle> oxidArticle = _dbContext.Oxarticles
                                 .Where(x => x.Oxartnum == artikelnummer)
                                 .ToList();

erhalte ich folgende Ausnahme:

MySqlConnector.MySqlConversionException
 HResult=0x80131500
 Nachricht = Cannot convert MySqlDateTime to DateTime when IsValidDateTime is false.
 Quelle = MySqlConnector
 Stapelüberwachung:
  bei MySqlConnector.MySqlDateTime.GetDateTime()
  bei MySqlConnector.Core.Row.GetDateTime(Int32 ordinal)
  bei MySqlConnector.MySqlDataReader.GetDateTime(Int32 ordinal)
  bei Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.Enumerator.MoveNext()
  bei System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
  bei System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
  bei OxidCategories.DataAccess.Implementations.Oxobject2CategoryRepository.GetAll(String artikelnummer) in C:\Daten\Visual Studio\Projects\Nowula\OxidCategories\OxidCategories\DataAccess\Implementations\Oxobject2CategoryRepository.cs: Zeile63
  bei OxidCategories.ViewModels.MainViewModel.SearchExecute(Object obj) in C:\Daten\Visual Studio\Projects\Nowula\OxidCategories\OxidCategories\ViewModels\MainViewModel.cs: Zeile248
  bei OxidCategories.Commands.RelayCommand.Execute(Object parameter) in C:\Daten\Visual Studio\Projects\Nowula\OxidCategories\OxidCategories\Commands\RelayCommand.cs: Zeile27
  bei System.Windows.Controls.Button.OnClick()
  bei System.Windows.Input.AccessKeyManager.ProcessKey(List`1 targets, String key, Boolean existsElsewhere, Boolean userInitiated)
  bei System.Windows.Input.AccessKeyManager.OnKeyDown(KeyEventArgs e)
  bei System.Windows.Input.InputManager.RaiseProcessInputEventHandlers(Tuple`2 postProcessInput, ProcessInputEventArgs processInputEventArgs)
  bei System.Windows.Input.InputManager.ProcessStagingArea()
  bei System.Windows.Input.InputProviderSite.ReportInput(InputReport inputReport)
  bei System.Windows.Interop.HwndKeyboardInputProvider.ProcessKeyAction(MSG& msg, Boolean& handled)
  bei System.Windows.Interop.HwndSource.CriticalTranslateAccelerator(MSG& msg, ModifierKeys modifiers)
  bei System.Windows.Interop.HwndSource.OnPreprocessMessage(Object param)
  bei System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
  bei System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
  bei System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
  bei System.Windows.Interop.HwndSource.OnPreprocessMessageThunk(MSG& msg, Boolean& handled)
  bei System.Windows.Interop.ComponentDispatcherThread.RaiseThreadMessage(MSG& msg)
  bei System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
  bei System.Windows.Application.RunDispatcher(Object ignore)
  bei System.Windows.Application.RunInternal(Window window)
  bei OxidCategories.App.Main()

Was ich versucht habe:

Alle Datumsfelder im Modell auf nullable zu stellen wie z. B.:

/// <summary>
/// Creation time
/// </summary>
public DateOnly? Oxinsert { get; set; }

/// <summary>
/// Timestamp
/// </summary>
public DateTime? Oxtimestamp { get; set; }

ValueConverter für die betroffnen Felder wie z. B.

namespace OxidCategories.Models.DatabaseModels;

public partial class NowulaOxidDbContext
{
   partial void OnModelCreatingPartial(ModelBuilder modelBuilder)
   {
       //
       // DateOnly-Konverter
       //
       var dateOnlyConverter = new DateOnlyConverter();

       modelBuilder.Entity<Oxarticle>()
           .Property(o => o.Oxdelivery)
           .HasConversion(dateOnlyConverter);
       modelBuilder.Entity<Oxarticle>()
           .Property(o => o.Oxinsert)
           .HasConversion(dateOnlyConverter);
       //
       // DateTime-Konverter
       //
       var dateTimeConverter = new DateTimeConverter();

       modelBuilder.Entity<Oxarticle>()
           .Property(o => o.Oxactivefrom)
           .HasConversion(dateTimeConverter);
       modelBuilder.Entity<Oxarticle>()
           .Property(o => o.Oxactiveto)
           .HasConversion(dateTimeConverter);
       modelBuilder.Entity<Oxarticle>()
           .Property(o => o.Oxtimestamp)
           .HasConversion(dateTimeConverter);
       modelBuilder.Entity<Oxarticle>()
           .Property(o => o.Oxupdatepricetime)
           .HasConversion(dateTimeConverter);
   }
}
/// <summary>
/// ValueConverter für DateOnly. Dieser Konverter wird verwendet, um "0000-00-00"
/// aus der Datenbank in ein gültiges C#- bzw. .NET-Datum zu konvertieren.
/// </summary>
public class DateOnlyConverter : ValueConverter<DateOnly, DateTime>
{
   private static readonly TimeOnly midnight = new TimeOnly(0, 0, 0);

   public DateOnlyConverter() : base(
       dateOnly => ConvertToDate(dateOnly),
       dateTime => ConvertToDateOnly(dateTime))
   { }

   /// <summary>
   /// Konvertiert ein DateOnly in ein DateTime.
   /// </summary>
   /// <param name="dateOnly"></param>
   /// <returns></returns>
   private static DateTime ConvertToDate(DateOnly dateOnly)
   {
       return dateOnly.ToDateTime(midnight);
   }

   /// <summary>
   /// Konvertiert ein DateTime in ein DateOnly.
   /// </summary>
   /// <param name="dateTime"></param>
   /// <returns></returns>
   private static DateOnly ConvertToDateOnly(DateTime dateTime)
   {
       if (dateTime == DateTime.MinValue)
       {
           return DateOnly.MinValue;
       }

       return DateOnly.FromDateTime(dateTime);
   }
}

/// <summary>
/// ValueConverter für DateTime. Dieser Konverter wird verwendet, um "0000-00-00 00:00:00"
/// aus der Datenbank in ein gültiges C#- nbzw. .NET-Datum zu konvertieren.
/// </summary>
public class DateTimeConverter : ValueConverter<DateTime, DateTime>
{
   public DateTimeConverter() : base(
       dt => ConvertToDatabaseDateTime(dt),
       dt => ConvertToApplicationDateTime(dt))
   { }

   /// <summary>
   /// Wandelt ein DateTime in ein gültiges Datenbank-Datum um.
   /// </summary>
   /// <param name="dateTime"></param>
   /// <returns></returns>
   private static DateTime ConvertToDatabaseDateTime(DateTime dateTime)
   {
       // Wenn in der Anwendung DateTime.MinValue als ungültig gilt, könnten wir
       // es in einen akzeptablen Wert für die Datenbank umwandeln wie z. Beispiel:
       // return DateTime.MinValue;
       // Wenn wir es jedoch unverändert lassen möchten, geben wir es einfach zurück:
       return dateTime;
   }

   /// <summary>
   /// Wandelt ein Datenbank-Datum in ein gültiges Anwendungs-Datum um.
   /// </summary>
   /// <param name="dateTime"></param>
   /// <returns></returns>
   private static DateTime ConvertToApplicationDateTime(DateTime dateTime)
   {
       if (dateTime == DateTime.MinValue)
       {
           // Handhabung für "0000-00-00 00:00:00" aus der Datenbank. Wir können hier z. B.
           // DateTime.MinValue oder einen anderen Standardwert zurückgeben.
           return DateTime.MinValue;
       }

       return dateTime;
   }
}

Da alles nicht fruchten will, habe ich mich entschlossen, die Gurus hier zu fragen.

Wenn ich ADO.NET nutze, dann reicht es, wenn ich in der Verbindungszeichenkette Allow Zero Datetime=True angebe.

Vielleicht hat mir jemand von euch einen Tipp.

Danke und liebe Grüße

René

29.09.2023 - 15:27 Uhr

Zitat von Caveman

Ja, es geht darum, dass sich nicht die konsumierenden Klasse um die Aufgaben kümmern müssen, sondern diese Verantwortichkeiten in der jeweiligen Klasse bleibt. Bin aber nicht so der Erklärbär 😃

Habe mal in Anlehnung an deinem Beispiel was gebastelt.

Schönes und einfaches Beispiel – doch ein Erklärbär 😉

Danke!

René

16.08.2023 - 00:25 Uhr

Alles unter .NET 7.0

Gegeben ist eine eigene Bibliothek isential.crypt.dll, die keine externen Abhängigkeiten enthält. Daraus wurde ein NuGet-Paket erstellt und in einer lokalen Paketquelle gespeichert.

Eine zweite eigene Bibliothek namens isential.MySqloverSsh.dll benutzt drei NuGet-Paketen:

  • isential.crypt
  • MySql.Data
  • SSH.NET

Daraus erstelle ich ein weiteres NuGet-Paket namens isential.MySqloverSsh und speichere es ebenfalls in derselben lokalen Paketquelle ab.

Die isential.MySqloverSsh.csproj sieht so aus:

<Project Sdk="Microsoft.NET.Sdk">
 <PropertyGroup>
   <TargetFramework>net7.0-windows8.0</TargetFramework>
   <ImplicitUsings>enable</ImplicitUsings>
   <Nullable>enable</Nullable>
   
   <!-- NuGet-Paket-Metadaten -->
   <PackageId>isential.MySqloverSsh</PackageId>
   <Authors>isential gmbh</Authors>
   <Description>Stellt eine MySql-Verbindung über einen SSH-Tunnel her und öffnet diese gleichzeitig.</Description>
   <PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
   <PackageProjectUrl>https://isential.de</PackageProjectUrl>
   <RepositoryUrl>https://isential.de</RepositoryUrl>
   <RepositoryType>Privat</RepositoryType>
   <PackageTags>isential MySqloverSsh</PackageTags>
   <AssemblyName>isential.MySqloverSsh</AssemblyName>
   <RootNamespace>isential.MySqloverSsh</RootNamespace>
   <Title>isential.MySqloverSsh</Title>
   <Version>1.0.0</Version>
   <Company>isential gmbh</Company>
   <Product>isential.MySqloverSsh</Product>
   <Copyright>(C) isential gmbh - alle Rechte vorbehalten.</Copyright>
 </PropertyGroup>
 
 <ItemGroup>
   <PackageReference Include="isential.crypt" Version="1.0.0" />
   <PackageReference Include="MySql.Data" Version="8.1.0" />
   <PackageReference Include="SSH.NET" Version="2020.0.2" />
 </ItemGroup>
</Project>

Das NuGet-Paket wird fehlerfrei Erstellt.

Ich binde dieses NuGet-Paket in das Ziel-Projekt ein. Dabei handelt es sich um ein WPF-Projekt unter .NET 7.0.

Der NuGet-Paketmanager zeigt mir auch alles richtig an:

Abhängigkeiten
    net7.0
        MySql.Data (>=8.1.0)
        SSH.NET (>= 2020.0.2)
        isential.crypt (>= 1.0.0)

Wie man sieht, wird auch isential.crypt als Abhängigkeit aufgeführt. Allerdings habe ich nach dem Einbinden in der Projektmappe unter "Pakete" folgendes Bild:

Pakete
    isential MySqloverSsh (1.0.0)
        Kompilietzeitassemblys
        Inhaltsdateien
        Dokumente
        MySql Data (8 1 0)
        SSH NET (2020.0.2)

Wie man sieht, fehlt isential.crypt (1.0.0).

Ich habe versuchshalber die Bibliothek isential.crypt.dlldirekt in isential.MySqloverSsh als DLL bzw. Projektverweis eingebunden und trotzdem steht diese im Zielprojekt nicht zur Verfügung. Klar kann ich isential.crypt.dll in das Zielprojekt manuell einbinden, aber ich möchte das lieber über isential.MySqloverSsh  machen, um mich nicht um die Abhängigkeiten kümmern zu müssen – das soll über das Paket isential.MySqloverSsh  automatisch gehen.

Warum passiert das und wie kann ich das lösen?

Danke un lG

René

28.06.2023 - 11:46 Uhr

Vielen Dank euch beiden – das hat mir weitergeholfen.

Das es sich bei meinem Teil-Projekt um eine einfache ASP.NET-Anwendung handelt, die auch von wenigen Usern verwendet wird, spielt die Serverauslastung keine große Rolle. Auf der anderen Seite kann ich nicht ausschließen, dass demnächst Betriebsdaten verwaltet werden, die sich zu einer Person zurückführen lassen. Aus diesen Gründen habe ich mich entschieden, alle externen Ressourcen durch lokale Installationen zu ersetzen.

Nun habe ich gestern das gemacht und dabei gleich die neuesten Versionen bereitgestellt – da bin ich layoutmäßig mit Bootstrap gleich auf die Schnauze gefallen: Der Versionssprung auf die neueste Version 5.3.0 hat mir viel Arbeit beschert, bis da Layout endlich wieder stand. Erschwerend kommt meine fehlende Erfahrung auf diesem Gebiet hinzu.

Es war mir aber wichtig, von erfahrenen Leute zu hören, wie sie dazu stehen. Nochmals danke für eure Beiträge!

LG

René

PS: Auch hier hatte ich meine Frage zusätzlich gestellt: https://learn.microsoft.com/en-us/answers/questions/1320065/self-hosting-resources-in-an-asp-net-core-mvc-appl

27.06.2023 - 18:36 Uhr

Danke T-Virus!

Mir fehlt einfach die Erfahrung. Sicher kann ich alles lokal ablegen und selbst ausliefern. Ich wollte aber auf die Erfahrung alter Hasen zurückgreifen, denn ich muss und will nicht das Rad neu erfinden. Im Geheimnis hatte ich gehofft, dass jeder laut sagt, das Einbinden über CDN sei genau das Richtige 😁 Aber auch wenn das so wäre, haben wir hier in der EU die Datenschutzgrundverordnung.

Dann muss ich mich damit abfinden, zusammen mit der Applikation tausende von Dateien mit auszuliefern.

Ich könnte mir vorstellen, dass die Abhängigkeiten ein Problem beim Selbsthosting werden könnten: Wenn andere Bibliotheken oder Frameworks Bootstrap, jQuery & Co. über ein CDN verwenden, könnte dies beim Selbsthosting zu Kompatibilitätsproblemen führen.

Ich weiß es nicht – wahrscheinlich werde ich nur wegen der EU-DSGVO doch diese Ressourcen selbst hosten müssen. Die CDN-Variante wäre mir aber in der Tat lieber.

Dank+LG

René

27.06.2023 - 17:39 Uhr

Hallo,

wie ich vor ein paar Wochen erzählte, bin ich dabei, mir ASP.NET-Core-MVC anzueignen. Das ist für mich auch nach so vielen Jahren in der Softwareentwicklung ein ganz neues und bisher unbekanntes Gebiet.

Nun bin ich mit meiner Anwendung ziemlich weit gekommen, dennoch bleiben bei mir einige Fragen unbeantwortet. Zwei davon betreffen externe Ressourcen – ich verwende bisher in meiner Anwendung folgende:

  • Bootstrap
  • jQuery
  • DataTables
  • FontAwesome

Ich hoffe, ich habe nichts vergessen.

  1. Technische Überlegung
    Was ist gemeinhin die gängige Vorgehensweise bei der Einbindung solcher Ressourcen? Selbst hosten oder über ein CDN einbinden?
  2. Datenschutzrechtliche Überlegung
    Auf jeden Fall ist das Einbinden über ein CDN bequemer. Abgesehen von den technischen Vor- und Nachteilen frage ich mich, wie das unter dem datenschutzrechtlichen Aspekt zu beurteilen ist. Seit der Einführung der EU-DSGVO gilt es, Sachen zu beachten, die früher eine untergeordnete Rolle gespielt haben – ich erinnere mich an die Google-Fonts.

Ich würde mich über eure Meinungen dazu sehr freuen.

LG

René

10.06.2023 - 10:45 Uhr

OK, macht Sinn. Dann fokussiere ich mich anders: Ich trenne die Modellschicht komplett von der Darstellungsschicht, indem ich View-Models verwende. Diese enthalten nur die Eigenschaften, die schließlich in der Ansicht angezeigt werden sollen.

Aber auch wenn ich das mache, muss ich wahrscheinlich immer noch sicher gehen, in der Datenbank nur die Felder zu aktualisieren, die auch geändert worden sind. In meinem vorigen Beispiel könnte ich z. B. grob so vorgehen:

public class ProduktionViewModel
{
    public string VzAnummer { get; set; }
    public string VzNummer { get; set; }
    public string VzBezeichnung { get; set; }
    public string VzKurzbez { get; set; }
    public int VzZuDisponieren { get; set; }
    public int ProduziertMenge { get; set; }
    public string Status { get; set; }
    public DateTime Geaendert { get; set; }
}

Und auf der View:

@model ProduktionViewModel

@{
    ViewData["Title"] = "Produktion ändern";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<!-- Rest des Codes -->

Ich konnte das noch nicht überprüfen, da ich zu einem Geburtstag muss 😉 Was geschieht, wenn ich in der View ein Feld auslasse? Ich gehe davon aus, dass dieses dann Null oder einen Default-Wert hat. Aber das kann ich erst morgen oder am Montag (je nachdem, wie feucht der Geburtstag war) überprüfen.

Danke und ein schönes Wochenende!

René

09.06.2023 - 22:54 Uhr

Zitat von Abt

Gibt es eine Möglichkeit, festzustellen, welche Formularfelder manuell geändert wurden?

Nein. Dafür gibts keinen Automatismus. Musst selbst implementieren.
HTTP kennt nur: Wert übertragen, oder nicht.

OK. Dann muss eine andere Lösung her.

Die Update-Methode (hier _InProduktionRepository.Update(inprodukktion)) hat keine Ahnung vom aktuellen Kontext. Diese sollte autark feststellen können, welche Felder geändert wurden.

Das kann die Methode ohne hellseherische Kräfte nicht leisten.

Das versteht sich vom selbst. Ich bin auf ModelState.GetFieldValidationState gestoßen. In Verbindung mit einer Feldwertabfrage auf wedernull noch default dürfte es möglich sein, Änderungen festzustellen.

// Ist das Feld gültig und wurde es geändert?
if (ModelState.GetFieldValidationState(fieldName) == Microsoft.AspNetCore.Mvc.ModelBinding.ModelValidationState.Valid)
{
    // Zusätzlich überprüfen, dass der Wert weder null oder der Standardwert ist.
    var field = inproduktion.GetType().GetProperty(fieldName)?.GetValue(inproduktion, null);

    if (field != null && field != default)
    {
        // An dieser Stelle sind wir sicher, dass das Feld geändert wurde
        // und einen gültigen Wert enthält. Daher kann das das ursprüngliche
        // Feld überschrieben werden.

    }
}

Noch nicht geprüft. Wenn das so funktioniert, würde ich den Code generisch implementieren.

Oder geht man schließlich ganz anders vor?

Ja. Man geht grundlegend anders vor. Angefangen damit, dass man keine Datenbank-Entitäten in der Oberfläche verwendet. Das gehört zu den absoluten Grundlagen.
[Artikel] Drei-Schichten-Architektur

OK, vieles ist bereits in Schichten implementiert und vieles auch abstrahiert. Das ist aber nicht das Thema meiner Frage. Ich bin am Lernen und nun stehe ich vor einem der vielen Probleme. Ein Schritt nach dem anderen – deine Tipps werde ich mir schon zu Herzen nehmen.

Man verwendet - wie bei jeder anderen Technologie - abstrahierte Modelle, im Web-Kontext meist Request- und Submit-Models genannt. Was Du da mit Deinen Models und Entitäten tust fällt leider eher in die Bezeichnung "Gefrickel". Gelinde gesagt machst Du das, was man allein aus Logik/Sicherheitsgründen niemals tun sollte: blind Modelle aus einem Request annehmen und in eine DB stecken.

Der korrekte Weg wäre extra Update-Modelle zu verwenden, die dann mit einer Patch-Operation mitgeteilt werden.
Property im Patch = null → keine Änderung

Willst Du alles über einen Post-Request lösen, dann brauchst trotzdem eigene Modelle, aber auch eine eigene Logik zur Verarbeitung von Eigenschaften eines DB Modells.

Ich nutze bereits Repositories, die auf extra dafür implementierten Schnittstellen basieren. Diese abstrahieren bereits den Datenbankzugriff in einer extra Schicht.

Es gibt sicher Luft nach oben, wie bereits erwähnt, ein Schritt nach dem anderen.

Nochmals vielen Dank!

René

09.06.2023 - 21:02 Uhr

Hallo,

ich mühe mich mit dem Thema ASP.Net-MVC ab, da es für mich neu ist.

In einem Formular gebe ich nur einige Felder eines Datensatzes aus. Eines davon kann vom Anwender geändert werden:

@model Protein_Models.Inproduktion

@{
    ViewData["Title"] = "Produktion ändern";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

@{
    string Geaendert		= Model.Geaendert.ToString("d")		?? string.Empty;
    string Synchronisiert	= Model.Synchronisiert.ToString()	?? string.Empty;
}

<form method="post" asp-action="Edit">
    <div class="border p-3">
        <div class="form-group-row">
            <h2 class="pb-2">Produktion @Model.Barcode</h2>
        </div>
        <div class="container">
            <div class="row">
                <div class="col-md-4">
                    <label asp-for="VzAnummer" class="form-label">Auftragsnummer</label>
                </div>
                <div class="col-md-8">
                    <input asp-for="VzAnummer" class="form-control-plaintext" readonly />
                </div>
                
                <div class="col-md-4">
                    <label asp-for="VzNummer" class="form-label">Artikelnummer</label>
                </div>
                <div class="col-md-8">
                    <input asp-for="VzNummer" class="form-control-plaintext" readonly />
                </div>
                
                <div class="col-md-4">
                    <label asp-for="VzBezeichnung" class="form-label">Artikelbezeichnung</label>
                </div>
                <div class="col-md-8">
                    <input asp-for="VzBezeichnung" class="form-control-plaintext" readonly />
                </div>
                
                <div class="col-md-4">
                    <label asp-for="VzKurzbez" class="form-label">Artikelkurzbezeichnung</label>
                </div>
                <div class="col-md-8">
                    <input asp-for="VzKurzbez" class="form-control-plaintext" readonly />
                </div>
                
                <div class="col-md-4">
                    <label asp-for="VzZuDisponieren" class="form-label">Auftragsmenge</label>
                </div>
                <div class="col-md-8">
                    <input asp-for="VzZuDisponieren" class="form-control-plaintext" readonly />
                </div>
                
                <div class="col-md-4">
                    <label asp-for="ProduziertMenge" class="form-label">Produzierte Menge</label>
                </div>
                <div class="col-md-8">
                    <input asp-for="ProduziertMenge" class="form-control"/>
                </div>
                
                <div class="col-md-4">
                    <label asp-for="Status" class="form-label">Status</label>
                </div>
                <div class="col-md-8">
                    <input asp-for="Status" class="form-control-plaintext" readonly />
                </div>
                
                <div class="col-md-4">
                    <label asp-for="Synchronisiert"	class="form-label">Synchronisiert am</label>
                </div>
                <div class="col-md-8">
                    <input asp-for="@Synchronisiert" class="form-control-plaintext" readonly />
                </div>
                
                <div class="col-md-4">
                    <label asp-for="Ersteller" class="form-label">Erstellt von</label>
                </div>
                <div class="col-md-8">
                    <input asp-for="Ersteller" class="form-control-plaintext" readonly />
                </div>
                
                <div class="col-md-4">
                    <label asp-for="Erstellt" class="form-label">Erstellt am</label>
                </div>
                <div class="col-md-8">
                    <input asp-for="Erstellt" class="form-control-plaintext" readonly />
                </div>
                
                <div class="col-md-4">
                    <label asp-for="Username" class="form-label">Zuletzt geändert von</label>
                </div>
                <div class="col-md-8">
                    <input asp-for="Username" class="form-control-plaintext" readonly />
                </div>
                
                <div class="col-md-4">
                    <label asp-for="Geaendert" class="form-label">Zuletzt geändert am</label>
                </div>
                <div class="col-md-8">
                    <input asp-for="@Geaendert" class="form-control-plaintext" readonly />
                </div>
            </div>
        </div>
        <div class="col-md-12">
            <div class="row justify-content-end">
                <div class="col-auto">
                    <button type="submit" class="btn btn-primary btn-lg" data-toggle="tooltip" data-placement="top" title="Speichern"><i class="fas fa-save"></i></button>
                    <a asp-controller="InProduktion" asp-action="Index" class="btn btn-success btn-lg ml-2" data-toggle="tooltip" data-placement="bottom" title="Zurück"><i class="fas fa-arrow-left"></i></a>
                </div>
            </div>
        </div>
    </div>
</form>

Das Modell Inproduktion hat weitere Felder, die man aber an dieser Stelle nicht braucht.

Im Controller habe ich eine Methode, in der das Update stattfindet.

[HttpPost]
public IActionResult Edit(Inproduktion inprodukktion)
{
    _InProduktionRepository.Update(inprodukktion);
    _InProduktionRepository.Save();
    return RedirectToAction("Index", "InProduktion");
}

Wenn ich mir jetzt inproduktion (das, was vom Formular kommt) anschaue,  sind nur die Felder mit sinnvollen Werten gesetzt, die auch im Formular verwendet wurden. Alle anderen haben Null oder einen Standardwert. Das finde ich unpraktisch.

Gibt es eine Möglichkeit, festzustellen, welche Formularfelder manuell geändert wurden?

Oder ist es möglich, das Verhalten so zu abzuändern, dass der gesamte Datensatz inproduktion zurückgegeben wird, also die Feldwerte, die im Formular nicht verwendet wurden mit ihren Datenbankwerten?

Oder geht man schließlich ganz anders vor?

Hintergrund:
Die Update-Methode (hier _InProduktionRepository.Update(inprodukktion)) hat keine Ahnung vom aktuellen Kontext. Diese sollte autark feststellen können, welche Felder geändert wurden. Wenn jedoch nur die Felder mit sinnvollen Werten gesetzt sind, die auch im Formular verwendet wurden, während alle anderen Null oder einen Standardwert aufweisen, entsteht Quark mit Soße.

Ich hoffe, ich konnte das einigermaßen erklären.

Danke und liebe Grüße

René

17.05.2023 - 16:24 Uhr

Danke euch beiden. @Abt, das Neuerstellen mit dotnet dev-certs https --trust war der Bringer.

Liebe Grüße und einen schönen Feiertag

René

17.05.2023 - 15:11 Uhr

Hallo!

Ich entwickle zu ersten Mal ein ASP.NET-Core-MVC-Projekt. Leider musste ich mittendrin aufhören, um etwas anderes zu machen. Nach über dreiWochen wollte ich das Projekt wieder angehen. Beim Ausführen in Visual Studio startet Edge, wie gewohnt, zeigt mir aber folgendes:

Ihre Verbindung ist nicht privat.

Angreifer versuchen möglicherweise Ihre Informationen von localhost zu stehlen (z. B. Kennwörter, Nachrichten oder Kreditkarten).

NET::ERR_CERT_INVALID

AktualisierenErweitert ausblenden

localhost schützt Ihre Daten mithilfe von Verschlüsselung. Als Microsoft Edge dieses Mal versuchte, eine Verbindung zu localhost herzustellen, hat die Website ungewöhnliche und falsche Anmeldedaten zurückgesendet. Das kann vorkommen, wenn ein Angreifer versucht, sich als localhost auszugeben, oder wenn ein WLAN-Anmeldebildschirm die Verbindung unterbrochen hat. Ihre Daten sind nach wie vor sicher, da Microsoft Edge die Verbindung abgebrochen hat, bevor Daten ausgetauscht wurden.

Sie können localhost im Moment nicht besuchen, da die Website verschlüsselte Anmeldeinformationen gesendet hat, die Microsoft Edge nicht verarbeiten kann. Netzwerkfehler und Angriffe sind in der Regel vorübergehend, sodass die Seite später wahrscheinlich wieder funktioniert.

Was ist passiert? Davor lief alles fehlerfrei.

Danke und liebe Grüße

René

30.04.2023 - 14:34 Uhr

OK, danke euch für die Tipps. Schließlich habe ich mich für Declaration Alignment entschieden, da dies genau das macht, was ich brauche, ohne viel umstellen oder lernen zu müssen. Allerdings musste ich die Ausrichtung durch Tabulatoren auf Ausrichtung durch Leerzeichen umstellen, denn ansonsten kommt das Tool ab und zu damit durcheinander. Das hat auch der Tool-Autor bestätigt und gleich ein Update nachgeschoben, welches aber dieses Verhalte nicht richtig abstellt. Für mich in Ordnung.

Das Tool Code alignment scheint um einiges leistungsfähiger zu sein, mit aber einer höheren Lernkurve. Ich konnte z. B. einen Block Mit Leerzeilen und Zeilenkommentaren

	// ISBN des Buches.
	public string ISBN { get; set; }
	
	// Titel des Buches.
	public string Title { get; set; }
	
	// Autor des Buches.
	public string Author { get; set; }
	
	// Verlag des Buches.
	public string Publisher { get; set; }
	
	// Erscheinungsjahr des Buches.
	public int Year { get; set; }
	
	// Seitenanzahl des Buches.
	public int Pages { get; set; }
	
	// Preis des Buches.
	public decimal Price { get; set; }

nicht richtig ausrichten. Sicher kann man das auch hinbekommen, aber da das erste Tool das gleich konnte, habe ich mich für das erste entschieden. Sollte ich aber mehr brauchen oder unlösbare Fehler im Tool finden, werde ich mich mit Code alignment näher befassen.

Ein schönes Wochenende und nochmals vielen Dank!

René

30.04.2023 - 13:17 Uhr

Danke. Wird es aber weiterentwickelt? Und wo finde ich eine (Kurz-)Anleitung?

LG

René

Edit: Sehe ich gerade: Home · cpmcgrath/codealignment Wiki (github.com)

28.04.2023 - 14:50 Uhr

Danke! Manchmal hat man eine Blockade und einem fällt kein passender Suchbegriff ein.

Dass ich andere Probleme habe, ist es klar. Wer nicht? Ich hab's nu aber scheee 😉

Schönes Wochenende!

René

27.04.2023 - 21:30 Uhr

Hallo,

gibt es für Visual Studio Tools zur Codeformatierung, die mir dabei helfen, aus z. B.

public class Book
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Author { get; set; }
    public int Pages { get; set; }
    public DateTime Published { get; set; }
    public string Isbn { get; set; }

    public Book(string title, string author, int pages, DateTime published, string isbn)
    {
        Title = title;
        Author = author;
        Pages = pages;
        Published = published;
        Isbn = isbn;
    }
}

folgendes zu machen?

public class Book
{
    public int		Id		{ get; set; }
    public string	Title		{ get; set; }
    public string	Author		{ get; set; }
    public int		Pages		{ get; set; }
    public DateTime	Published	{ get; set; }
    public string	Isbn		{ get; set; }

    public Book(string title, string author, int pages, DateTime published, string isbn)
    {
        Title		= title;
        Author		= author;
        Pages		= pages;
        Published	= published;
        Isbn		= isbn;
    }
}

Sicher ein bisschen Luxusproblem, aber ich habe es nun schön ordentlich... 😃

Danke und liebe Grüße

René