Laden...
Avatar #avatar-3521.gif
pollito myCSharp.de - Member
Dipl.-Ing. Ingenieurinformatik 78647 Trossingen Dabei seit 26.02.2010 314 Beiträge

Forenbeiträge von pollito Ingesamt 314 Beiträge

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é

18.04.2023 - 10:02 Uhr

Ich empfinde das ganze Thema als Geschmacksache. Das obige Beispiel lässt sich gut lesen und erfordert keinerlei Überlegungen. Dasselbe gilt für nachstehenden Code:

if (bedingung == true)

oder

if (bedingung == false)

Ich selbst empfinde Lesbarkeit als sehr wichtig, auch wenn man dafür mehr tippen muss.

if (bedingung)

oder

if (!bedingung)

Auch damit habe ich keine Probleme, auch wenn ich selbst die ersten zwei obig aufgeführten Varianten bevorzuge.

11.04.2023 - 12:14 Uhr

Zitat von Palladin007

Entweder die Nummerierung auch am Anfang, oder einen "Springe zum letzten Beitrag" Button am Anfang.
Am besten beides 😃

Beides wäre sehr sinnvoll.

11.04.2023 - 09:04 Uhr

Zitat von BhaaL

Ists eigentlich Absicht, dass ein klick auf "Sammelthema Wünsche und Bugreports myCSharp " in der Foren-Übersicht nicht zum letzten Beitrag springt, sondern zum ersten? Das ist irgendwie...merkwürdig.

In diesem Zusammenhang sollte man auch überlegen, die Seitennummerierung (1 2 3 4 5 Letzte >) am Seitenende auch am Seitenanfang zu wiederholen. Gerade wenn man mit dem Handy unterwegs ist, muss man wie verrückt nach unten scrollen, um diese Funktion zu bedienen.

Darüber hinaus sollte es in der Übersicht neben dem Themenlink einen Link zum letzten Beitrag geben.

08.04.2023 - 21:43 Uhr

Wurde hier schon das Thema "Private Mitteilungen" angesprochen? Ich wollte eine PM senden, es kam jedoch nur die nichts sagende Fehlermeldung "Hoppla" o. ä..

Ansonsten möchte ich daran erinnern, dass wir alle erwachsene Leute sind und uns demnach auch verhalten und äußern sollten –– das gehört auch zur Teamkultur.

07.04.2023 - 23:22 Uhr

Es gibt keine Geschlechtsabfrage in diesem Forum - und das schon seit Jahren. Und das bleibt auch so.
Herzlich Willkommen im Jahr 2023. 🤦‍♂️

Das weiß ich. Du bist der Chef – das heißt aber lange nicht, dass alles richtig ist, was du außerhalb der Technik machst bzw. meinst. Und mein Willkommen im Jahr 2023 habe ich bereits letzten 1. Januar bekommen.

Manchmal frage ich mich, ob z. B. gegenderte Prosa möglich ist. So viel zum Land der Dichter und Denker. Dann lieber ein generisches Femininum – dagegen hätte ich auch nichts. Dann gehöre ich zu den Entwicklerinnen. Aber Entwickler:in – was ist das für eine Sprache?

Ne, mein lieber Abt, das hat mit dem Jahr 2023 nichts zu tun. Es ist dein Forum, es sind deine Regeln, aber dann bitte konsequent: Warum steht in meinem Profil myCSharp.de - Member? Dann bitte myCSharp.de - Member:in 😉

Lassen wir das – es war eine Anregung und gut ist.

Nun aber, was ist mit dem ersten von mir aufgeführten Punkt?

07.04.2023 - 22:56 Uhr
  1. Thema als gelöst markieren
    Habe ich das nur noch nicht gefunden oder ist das auch im neuen Design nicht implementiert?

  2. GendernThemenstarter:in
    Ich möchte auf gar keinen Fall eine Diskussion darüber entfachten, aber muss das in einem technischen Forum sein und ist das auch von der Community breit gewollt? Gerade in der Technik stehe ich dem kritisch gegenüber, denn viele Abhandlungen, bei denen man konzentriert lesen muss, um ein Thema zu begreifen, erschwert das Gendern die Verständlichkeit.

    In diese speziellen Fall könnte man im Profil angeben, ob man Weibchen oder Männlein ist und automatisch Themenstarterin oder Themenstarter setzen.

Ansonsten ein gelungenes Design, mit ein paar Kinderkrankheiten, die zurzeit jedoch behoben werden, wie die vorigen Beiträge zeigen.

Grüße

René

28.03.2023 - 12:01 Uhr

Ich habe mir das Thema genauer angesehen. Dabei habe ich festgestellt, dass Windows immer dafür sorgt, dass die dpi-Zahl bei unterschiedlicher Skalierung gleich bleibt. Dazu ändert Windows die Anzahl der Pixel. Hier ein Beispiel von meinem Dell XPS mit 3840x2400 Pixeln: bei einer Skalierung von 175% rechnet Windows mit einer Bildschirmauflösung von 2194x1371. Das bedeutet, dass die Skalierung bei dieser Auflösung weiterhin 100% bei 96 dpi bleibt.

Trotzdem kommt unsere alte Software damit durcheinander. Ich vermute, dass sie selbst einen Skalierungsfaktor berechnet und sich dabei verrechnet. Eigentlich sollte die Software gar keine Skalierung berücksichtigen, solange Windows selbst eine Skalierung von 100% bei 96 dpi liefert.

Hier ist ein Stück Programmcode, der mir alles Wesentliche im Debugger zeigt (und bestätigt):


using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

namespace multimonitor1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            // Abrufen der DPI-Skalierung und Skalierungsstufe jedes Bildschirms
            List<(float, float, int)> screenDpiScales = new List<(float, float, int)>();

            while (true)
            {
                foreach (Screen screen in Screen.AllScreens)
                {
                    using (var form = new Form() { Bounds = screen.Bounds, TopMost = true })
                    {
                        form.Show();

                        Graphics g = Graphics.FromHwnd(form.Handle);

                        float	dpiX	= g.DpiX;
                        float	dpiY	= g.DpiY;
                        int		scale	= (int) Math.Round(dpiX / 96.0 * 100);
                        
						screenDpiScales.Add((dpiX, dpiY, scale));
                        
						form.Hide();
                    }
                }

                // Ermitteln der Größe und Position der Anwendung
                int appLeft		= 0;
                int appTop		= 0;
                int appWidth	= 0;
                int appHeight	= 0;

                // Hier müssen die genauen Koordinaten der Anwendung in Bezug auf den Bildschirm ermittelt werden.
                // *** [English] The exact coordinates of the application in relation to the screen must be determined here. ***

                // Berechnen der Größe und Position der Anwendung auf jedem Bildschirm
                for (int i = 0; i < Screen.AllScreens.Length; i++)
                {
                    Screen screen = Screen.AllScreens[i];

                    (float, float, int) dpiScale = screenDpiScales[i];

                    Rectangle screenBounds = screen.Bounds;

                    float	dpiX	= dpiScale.Item1;
                    float	dpiY	= dpiScale.Item2;
                    int		scale	= dpiScale.Item3;

                    appLeft		= (int) (appLeft	* screenBounds.Width	/ (dpiX / 96.0 * scale));
                    appTop		= (int) (appTop		* screenBounds.Height	/ (dpiY / 96.0 * scale));
                    appWidth	= (int) (appWidth	* screenBounds.Width	/ (dpiX / 96.0 * scale));
                    appHeight	= (int) (appHeight	* screenBounds.Height	/ (dpiY / 96.0 * scale));

                    // Ausgabe der Größe und Position der Anwendung
                    Console.WriteLine($"Left:   {appLeft}");
                    Console.WriteLine($"Top:    {appTop}");
                    Console.WriteLine($"Width:  {appWidth}");
                    Console.WriteLine($"Height: {appHeight}");
                }

                Console.ReadLine();
            }
        }
    }
}

Damit ist der Fall für mich gelöst.

Vielen Dank und beste Grüße

René

25.03.2023 - 10:00 Uhr

Danke, daran hatte ich auch gedacht. Allerdings bietet mein Hauptholster das in seinen Tarifen nicht, auch .Net nicht. Leider, denn ich war bisher mit ihm sehr zufrieden (all-inkl:, 2x Business-Webpakete und 1x managed Server).

24.03.2023 - 14:10 Uhr

Danke. Schöne Lektüre für ein verregnetes Wochenende.

LG

René

24.03.2023 - 13:57 Uhr

Hallo,

da bin ich heute wieder 🙂

Mit Hilfe eines Udemy-Kurses lerne ich gerade ASP.NET Core MVC. Der Kurs ist dieser hier:

https://www.udemy.com/course/aspnet-core-mvc-configuracion-e-implementacion-net-6/

Ich habe den Kurs noch nicht abgeschlossen, aber ich habe schon eine Frage: Ist es möglich, die ASP.NET-Core-MVC-Anwendung auf einem Linux-basierten Webspace zu hosten, auf dem .NET Core nicht installiert ist und zu allem Überfluss ich über keine Root-Rechte verfüge, um es zu installieren? Auf diesem Webspace kann ich Sachen wie Wordpress, Joomla, HTML+CSS usw. hosten. Ist es möglich, die ASP.NET-Core-MVC-Anwendung so zu veröffentlichen, dass alles, was benötigt wird, enthalten ist?

Vielen Dank im Voraus für Ihre Hilfe.

René

22.03.2023 - 09:51 Uhr

Danke! Ich bin dabei. Ich arbeite einen Udemy-Kurs über ASP.NET Core MVC durch und komme soweit zurecht. Dennoch bleibt noch ein Haufen zu lernen, aber mit Hilfe von SelfHTML und der Doku von Bootstrap kann ich mein (einfaches) Ziel erreichen. Es macht Lust auf mehr!

LG

René

19.03.2023 - 11:47 Uhr

Zur Aufteilung denke ich, dass ASP.NET MVC mir das vorgibt.
Nein, MVC gibt sowas prinzipiell nicht zwangsweise vor.

Von Zwang schrieb ich nicht. Ich halte mich an den strukturellen Empfehlungen und erfinde nichts neues, solange ich die Technologie nicht beherrsche.

18.03.2023 - 16:49 Uhr

Die Überlegung hatte ich auch, aber es schien mir damals so, dass die Lernkurve von Blazor für mein persönliches Vorhaben steiler als die von ASP.NET MVC ist. Aber das eine schließt das andere nicht aus. Nun habe ich mich nach langen Überlegungen und Abwägungen für ASP.NET MVC entschieden und wie es bisher aussieht, erfüllt das meine Anforderungen zu 100%. Ich komme gut voran, die dahinter stehende Idee finde ich transparent und es macht auch Spaß. Ob es in einigen Tagen immer noch Spaß macht, wenn ich weiter bin, wird sich zeigen.

Probleme habe ich eher mit HTML und CSS, da ich mich bisher nur peripher damit beschäftigt hatte. Ich bin mir aber sicher, mit Blazor hätte ich mit diesen zwei Kandidaten dieselben Probleme.

LG

René

17.03.2023 - 21:34 Uhr

ich habe damals u.a. anhand von
>
gelernt (zusätzlich hatte ich noch ein Buch über HTML 4).

Danke, das scheint eine gute Anlaufstelle zu sein. Daran hatte ich nicht mehr gedacht – vor vielen Jahren hatte ich Infos auf der Seite gefunden. Sie kommt jetzt in meine Sammlung.

Was CSS betrifft:
>
(wobei CSS auch Vererbung (engl. "inheritance") verwendet )
Eine Aufteilung des Webprojekts in separate html und css-Dateien kann auch hier Sinn haben um Änderungen/Erweiterungen leichter umsetzen zu können. Und bei komplexeren Seiten erst die Controls zeichnen lassen, danach in der gewünschten Formatierung (die css-Befehle also am Ende der Datei platzieren oder importieren lassen)

Danke. Ich habe einen steilen Weg vor mir. Zur Aufteilung denke ich, dass ASP.NET MVC mir das vorgibt.

Ich hoffe sehr, dass ich möglichst schnell wieder produktiv werde, da ich gerade in eine andere Welt als die für mich bekannte eintauche: ASP.NET Core, EF Core, HTML und CSS. Was ASP.NET Core (und ein bisschen EF) angeht, habe ich bei Udemy m. E. einen für mich guten Kurs gefunden, allerdings nicht für jeden geeignet, da in spanischer Sprache: ASP NET CORE MVC Configuración e Implementación (NET 6) – Aprenda ASP.NET Core 6 con MVC y Entity Framework Core mientras construimos un proyecto del mundo real.. Bisher komme ich damit gut zurecht.

Mit visuellen Sachen tue ich mir aber viel schwerer, da dies mir fast völlig fremd ist. Daher mein Post hier.

Danke euch beiden wieder!

LG

René