.Net MailMessage-Objekt als EML-Datei speichern: Unterschied zwischen den Versionen

Aus maxTechCorner
imported>TechCorner-Redaktion
Server-Software: Überarbeitung
imported>TechCorner-Redaktion
Überarbeitung: MailKit/MimeKit, Fix PickupDirectoryLocation, Kategorie Softwareentwicklung, Stand Oktober 2026
 
Zeile 1: Zeile 1:
== Symptom ==
{{Steckbrief
Von einem Webserver erzeugte E-Mails können wegen eines Fehlers (Netzwerk, Programm) nicht verschickt werden und gehen sofort verloren – das erschwert die Fehlersuche.
| gilt_fuer  = .NET 8/10, .NET Framework 4.8.x; System.Net.Mail und MailKit/MimeKit 4.x
| bereich    = Softwareentwicklung
| dauer      = ca. 15 Minuten
| rechte      = Schreibrechte im Ablageordner der Anwendung
| stand      = Oktober 2026
}}
'''Ein .NET-MailMessage-Objekt als EML-Datei speichern''' sichert E-Mails, die Ihre Anwendung wegen eines Fehlers nicht versenden konnte – samt Kopfzeilen und Anhängen. Statt nur einer Fehlermeldung im Log liegt die Original-Mail vor, lässt sich in Outlook oder Thunderbird öffnen und später erneut versenden.


== Ursache und Lösung ==
[[Datei:NET-MailMessage-EML.png|thumb|center|750px|Nicht versendete E-Mails als EML-Datei sichern: mit MailKit über WriteToAsync oder mit System.Net.Mail über ein Pickup-Verzeichnis mit absolutem Pfad.]]
Tritt ein Fehler beim Versand auf, lässt sich der Fehler zwar protokollieren, die Original-Mail ist aber verloren. Abhilfe: das <code>MailMessage</code>-Objekt für die Analyse als '''EML-Datei''' speichern. EML-Dateien enthalten die komplette Mail inkl. Kopfdaten und lassen sich mit Outlook oder Thunderbird öffnen. Über eine Erweiterungsklasse lässt sich das direkt umsetzen:
 
== Problem ==
* Von einem Webserver oder Dienst erzeugte E-Mails scheitern beim Versand (SMTP-Server nicht erreichbar, Authentifizierung, Zeitüberschreitung).
* Im Log steht nur die Exception – die eigentliche Mail ist verloren und der Fehler schwer nachzuvollziehen.
 
== Lösung ==
=== Variante 1: MailKit/MimeKit (empfohlen) ===
Microsoft empfiehlt <code>System.Net.Mail.SmtpClient</code> nicht mehr für neue Entwicklungen, weil er moderne Protokolle und Anmeldeverfahren (z. B. OAuth 2.0) nicht unterstützt. Der Standard ist '''MailKit''' mit dem Nachrichtenmodell '''MimeKit'''. Dort ist das Speichern als EML ein Einzeiler:
<syntaxhighlight lang="bash">
dotnet add package MailKit
</syntaxhighlight>
<syntaxhighlight lang="csharp">
using MailKit.Net.Smtp;
using MailKit.Security;
using MimeKit;
 
var message = new MimeMessage();
message.From.Add(MailboxAddress.Parse("shop@example.com"));
message.To.Add(MailboxAddress.Parse("kunde@example.com"));
message.Subject = "Ihre Bestellung";
message.Body = new TextPart("plain") { Text = "Vielen Dank für Ihre Bestellung." };
 
try
{
    using var smtp = new SmtpClient();
    await smtp.ConnectAsync("smtp.example.com", 587, SecureSocketOptions.StartTls);
    await smtp.AuthenticateAsync("benutzer", "passwort");
    await smtp.SendAsync(message);
    await smtp.DisconnectAsync(true);
}
catch (Exception ex)
{
    var dir = Path.Combine(AppContext.BaseDirectory, "mail-fehler");
    Directory.CreateDirectory(dir);
    var file = Path.Combine(dir, $"{DateTime.UtcNow:yyyyMMdd-HHmmss}-{Guid.NewGuid():N}.eml");
    await message.WriteToAsync(file);          // komplette Mail als EML sichern
    logger.LogError(ex, "Mailversand fehlgeschlagen, gesichert unter {File}", file);
}
</syntaxhighlight>
Eine gesicherte Mail lässt sich später mit <code>MimeMessage.Load(file)</code> wieder einlesen und erneut versenden.
 
=== Variante 2: System.Net.Mail (Bestandscode) ===
Für bestehende Anwendungen mit <code>MailMessage</code> funktioniert der Trick über das Pickup-Verzeichnis: Ein zweiter <code>SmtpClient</code> „versendet“ die Mail als Datei in einen Ordner.
<syntaxhighlight lang="csharp">
<syntaxhighlight lang="csharp">
using System.Net.Mail;
using System.Net.Mail;


namespace MaxIt.TechCorner
public static class MailMessageExtensions
{
{
     public static class MailMessageExtensions
    /// <summary>Speichert eine MailMessage als EML-Datei im angegebenen Ordner.</summary>
     public static void Archive(this MailMessage message, string directory)
     {
     {
         /// <summary>Stores a MailMessage as EML file.</summary>
         Directory.CreateDirectory(directory);
         public static void Archive(this MailMessage message)
         using var diskClient = new SmtpClient
         {
         {
             using (SmtpClient diskclient = new SmtpClient("mysmtphost"))
             DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory,
            {
            PickupDirectoryLocation = directory
                diskclient.DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory;
        };
                diskclient.PickupDirectoryLocation = @"%TEMP%\mails";
        diskClient.Send(message);
                diskclient.Send(message);
            }
        }
     }
     }
}
}
</syntaxhighlight>
</syntaxhighlight>
Durch <code>DeliveryMethod = SpecifiedPickupDirectory</code> wird die Mail beim „Versand“ als EML-Datei im Pfad <code>PickupDirectoryLocation</code> abgelegt. Verwendung im Fehlerfall:
Verwendung im Fehlerfall:
<syntaxhighlight lang="csharp">
<syntaxhighlight lang="csharp">
try
try
{
{
     smtpclient.Send(message);
     smtpClient.Send(message);
}
}
catch (Exception ex)
catch (SmtpException ex)
{
{
     message.Archive();   // Mail zur Fehlersuche als EML sichern
     message.Archive(Path.Combine(Path.GetTempPath(), "mail-fehler"));
    logger.LogError(ex, "Mailversand fehlgeschlagen");
}
}
</syntaxhighlight>
</syntaxhighlight>
So steht neben den Log-Meldungen auch die E-Mail selbst (inkl. Anhänge) zur Analyse bereit.


== Unterstützung von m.a.x. it ==
{{Warnung|<code>PickupDirectoryLocation</code> erwartet einen '''absoluten Pfad''' zu einem '''existierenden''' Ordner. Umgebungsvariablen wie <code>%TEMP%</code> werden nicht aufgelöst – deshalb <code>Path.GetTempPath()</code> bzw. <code>Directory.CreateDirectory</code> verwenden. Der Dateiname wird automatisch als GUID vergeben.}}
Bei diesem Thema unterstützt Sie die [https://www.max-it.de/development/individuelle-softwareentwicklung/ individuelle Softwareentwicklung] von m.a.x. it.
 
== Hinweise ==
* '''Datenschutz:''' EML-Dateien enthalten personenbezogene Daten. Ablageordner nur für das Dienstkonto lesbar machen, nicht unter <code>wwwroot</code> ablegen und alte Dateien automatisch löschen ([[DSGVO]]).
* '''Wiederholung:''' Für produktive Systeme eignet sich ein Hintergrunddienst, der den Ordner regelmäßig abarbeitet und gesicherte Mails erneut versendet.
* '''Microsoft 365:''' Der SMTP-Versand mit Basic Authentication wird bei Exchange Online abgeschaltet. MailKit unterstützt OAuth 2.0 bzw. den Versand über Microsoft Graph.
* Bei Entwicklung und Modernisierung von .NET-Anwendungen unterstützt Sie die [https://www.max-it.de/development/individuelle-softwareentwicklung/ individuelle Softwareentwicklung von m.a.x. it].
 
== Siehe auch ==
* [[SMTP]]
* [[WCF Rest-Service]]
* [[DSGVO]]
 
== Links und Quellen ==
* [https://learn.microsoft.com/de-de/dotnet/api/system.net.mail.smtpclient Microsoft Learn: SmtpClient-Klasse (mit Empfehlung für MailKit)]
* [https://www.max-it.de/development/individuelle-softwareentwicklung/ Individuelle Softwareentwicklung von m.a.x. it]


{{Uebermax}}
{{Uebermax}}
[[Kategorie:Server-Software]]
[[Kategorie:Softwareentwicklung]]



Aktuelle Version vom 14. Oktober 2026, 00:00 Uhr

Auf einen Blick

Gilt für.NET 8/10, .NET Framework 4.8.x; System.Net.Mail und MailKit/MimeKit 4.x
BereichSoftwareentwicklung
Dauerca. 15 Minuten
RechteSchreibrechte im Ablageordner der Anwendung
StandOktober 2026

Ein .NET-MailMessage-Objekt als EML-Datei speichern sichert E-Mails, die Ihre Anwendung wegen eines Fehlers nicht versenden konnte – samt Kopfzeilen und Anhängen. Statt nur einer Fehlermeldung im Log liegt die Original-Mail vor, lässt sich in Outlook oder Thunderbird öffnen und später erneut versenden.

Nicht versendete E-Mails als EML-Datei sichern: mit MailKit über WriteToAsync oder mit System.Net.Mail über ein Pickup-Verzeichnis mit absolutem Pfad.

Problem

  • Von einem Webserver oder Dienst erzeugte E-Mails scheitern beim Versand (SMTP-Server nicht erreichbar, Authentifizierung, Zeitüberschreitung).
  • Im Log steht nur die Exception – die eigentliche Mail ist verloren und der Fehler schwer nachzuvollziehen.

Lösung

Variante 1: MailKit/MimeKit (empfohlen)

Microsoft empfiehlt System.Net.Mail.SmtpClient nicht mehr für neue Entwicklungen, weil er moderne Protokolle und Anmeldeverfahren (z. B. OAuth 2.0) nicht unterstützt. Der Standard ist MailKit mit dem Nachrichtenmodell MimeKit. Dort ist das Speichern als EML ein Einzeiler:

dotnet add package MailKit
using MailKit.Net.Smtp;
using MailKit.Security;
using MimeKit;

var message = new MimeMessage();
message.From.Add(MailboxAddress.Parse("shop@example.com"));
message.To.Add(MailboxAddress.Parse("kunde@example.com"));
message.Subject = "Ihre Bestellung";
message.Body = new TextPart("plain") { Text = "Vielen Dank für Ihre Bestellung." };

try
{
    using var smtp = new SmtpClient();
    await smtp.ConnectAsync("smtp.example.com", 587, SecureSocketOptions.StartTls);
    await smtp.AuthenticateAsync("benutzer", "passwort");
    await smtp.SendAsync(message);
    await smtp.DisconnectAsync(true);
}
catch (Exception ex)
{
    var dir = Path.Combine(AppContext.BaseDirectory, "mail-fehler");
    Directory.CreateDirectory(dir);
    var file = Path.Combine(dir, $"{DateTime.UtcNow:yyyyMMdd-HHmmss}-{Guid.NewGuid():N}.eml");
    await message.WriteToAsync(file);          // komplette Mail als EML sichern
    logger.LogError(ex, "Mailversand fehlgeschlagen, gesichert unter {File}", file);
}

Eine gesicherte Mail lässt sich später mit MimeMessage.Load(file) wieder einlesen und erneut versenden.

Variante 2: System.Net.Mail (Bestandscode)

Für bestehende Anwendungen mit MailMessage funktioniert der Trick über das Pickup-Verzeichnis: Ein zweiter SmtpClient „versendet“ die Mail als Datei in einen Ordner.

using System.Net.Mail;

public static class MailMessageExtensions
{
    /// <summary>Speichert eine MailMessage als EML-Datei im angegebenen Ordner.</summary>
    public static void Archive(this MailMessage message, string directory)
    {
        Directory.CreateDirectory(directory);
        using var diskClient = new SmtpClient
        {
            DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory,
            PickupDirectoryLocation = directory
        };
        diskClient.Send(message);
    }
}

Verwendung im Fehlerfall:

try
{
    smtpClient.Send(message);
}
catch (SmtpException ex)
{
    message.Archive(Path.Combine(Path.GetTempPath(), "mail-fehler"));
    logger.LogError(ex, "Mailversand fehlgeschlagen");
}


WarnungPickupDirectoryLocation erwartet einen absoluten Pfad zu einem existierenden Ordner. Umgebungsvariablen wie %TEMP% werden nicht aufgelöst – deshalb Path.GetTempPath() bzw. Directory.CreateDirectory verwenden. Der Dateiname wird automatisch als GUID vergeben.

Hinweise

  • Datenschutz: EML-Dateien enthalten personenbezogene Daten. Ablageordner nur für das Dienstkonto lesbar machen, nicht unter wwwroot ablegen und alte Dateien automatisch löschen (DSGVO).
  • Wiederholung: Für produktive Systeme eignet sich ein Hintergrunddienst, der den Ordner regelmäßig abarbeitet und gesicherte Mails erneut versendet.
  • Microsoft 365: Der SMTP-Versand mit Basic Authentication wird bei Exchange Online abgeschaltet. MailKit unterstützt OAuth 2.0 bzw. den Versand über Microsoft Graph.
  • Bei Entwicklung und Modernisierung von .NET-Anwendungen unterstützt Sie die individuelle Softwareentwicklung von m.a.x. it.

Siehe auch

Links und Quellen


Über m.a.x. it Die m.a.x. Informationstechnologie AG ist seit über 30 Jahren IT-Partner mittelständischer und großer Unternehmen in München und bietet maßgeschneiderte Lösungen und Services in den Bereichen Cloud, Cybersecurity, Netzwerk, Windows, Linux und Softwareentwicklung. Sie haben eine Frage zu diesem Artikel oder brauchen Unterstützung? Kontakt aufnehmen