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

Aus maxTechCorner
Keine Bearbeitungszusammenfassung
imported>TechCorner-Redaktion
Überarbeitung: MailKit/MimeKit, Fix PickupDirectoryLocation, Kategorie Softwareentwicklung, Stand Oktober 2026
 
(6 dazwischenliegende Versionen von 2 Benutzern werden nicht angezeigt)
Zeile 1: Zeile 1:
==='''Symptom'''===
{{Steckbrief
E-Mail-Nachrichten, die von einem Webserver erzeugt werden, können wegen eines Fehlers (z. B. Netzwerkfehler, Programmfehler, etc.) nicht verschickt werden und gehen sofort verloren. Die Fehlersuche wird dadurch erheblich erschwert.
| gilt_fuer  = .NET 8/10, .NET Framework 4.8.x; System.Net.Mail und MailKit/MimeKit 4.x
==='''Ursache und Lösung'''===
| bereich    = Softwareentwicklung
Tritt ein Fehler beim Mailversand auf, kann der Fehler zwar mit den üblichen Logging-Mechanismen registriert werden, die Original-E-Mail aber ist verloren. Insbesondere wenn ein Netzwerkfehler vorliegt und die E-Mail daher auch nicht verdeckt (BCC) an Standardempfänger geschickt werden kann, bietet sich die Lösung an, das MailMessage-Objekt für die Fehlersuche als EML-Datei auf dem Server zu speichern. <br />
| dauer      = ca. 15 Minuten
EML-Dateien enthalten den gesamten Inhalt einer E-Mail und deren Kopfdaten, also z. B. auch Informationen über Absender und Empfänger. EML-Dateien können mit E-Mail-Programmen wie MS Outlook oder Mozilla Thunderbird direkt geöffnet werden.<br />
| rechte      = Schreibrechte im Ablageordner der Anwendung
Mithilfe der statischen Erweiterungsklasse „MailMessageExtensions“ kann ein MailMessage-Objekt direkt als EML-Datei gespeichert werden.
| stand      = Oktober 2026
<pre>using System;
}}
using System.Collections.Generic;
'''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.
using System.IO;
 
using System.Linq;
[[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.]]
using System.Net.Mail;
 
using System.Text;
== 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." };


namespace MaxIt.TechCorner
try
{
{
public static class MailMessageExtensions
    using var smtp = new SmtpClient();
{
    await smtp.ConnectAsync("smtp.example.com", 587, SecureSocketOptions.StartTls);
 
    await smtp.AuthenticateAsync("benutzer", "passwort");
/// <summary>
    await smtp.SendAsync(message);
/// Stores a MailMessage as EML file.
    await smtp.DisconnectAsync(true);
/// </summary>
}
/// <param name="message">The MailMessage object to be stored.</param>
catch (Exception ex)
public static void Archive(this MailMessage message)
{
{
    var dir = Path.Combine(AppContext.BaseDirectory, "mail-fehler");
using (SmtpClient diskclient = new SmtpClient("mysmtphost"))
    Directory.CreateDirectory(dir);
{
    var file = Path.Combine(dir, $"{DateTime.UtcNow:yyyyMMdd-HHmmss}-{Guid.NewGuid():N}.eml");
diskclient.DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory;
    await message.WriteToAsync(file);         // komplette Mail als EML sichern
diskclient.PickupDirectoryLocation = "%TEMP%\\mails";
    logger.LogError(ex, "Mailversand fehlgeschlagen, gesichert unter {File}", file);
diskclient.Send(message);
}
}
}
}
}
</pre>
</syntaxhighlight>
Dazu wird ein neues SmtpClient-Objekt erzeugt, dessen Option „DeliveryMethod“ den Wert „SmtpDe-liveryMethod.SpecifiedPickupDirectory“ erhält. Dadurch wird beim Versand das MailMessage-Objekt als EML-Datei gespeichert. Der Speicherort wird der Eigenschaft „PickupDirectoryLocation“ zugewiesen.<br />
Eine gesicherte Mail lässt sich später mit <code>MimeMessage.Load(file)</code> wieder einlesen und erneut versenden.
Verwendet wird die Erweiterung, indem deren Namespace eingebunden und im Code die Archive()-Methode für das MailMessage-Objekt aufgerufen wird:
<pre>System.Net.Mail.SmtpClient smtpclient;
System.Net.Mail.MailMessage message;


// ...
=== 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">
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);
    }
}
</syntaxhighlight>
Verwendung im Fehlerfall:
<syntaxhighlight lang="csharp">
try
try
{
{
// Try to send message
    smtpClient.Send(message);
smtpclient.Send(message);
}
}
catch (Exception ex)
catch (SmtpException ex)
{
{
// Archive message
    message.Archive(Path.Combine(Path.GetTempPath(), "mail-fehler"));
message.Archive();
    logger.LogError(ex, "Mailversand fehlgeschlagen");
}
}
</pre>
</syntaxhighlight>
Zusätzlich zu fehlerbeschreibenden Logging-Nachrichten steht so auch die E-Mail selbst, inklusive eventuell vorhandener Dateianhänge, für die Fehlersuche zur Verfügung.
 
{{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.}}


==='''Weitere Informationen'''===
== Hinweise ==
Mehr Informationen zur Anwender- und Netzwerkbetreuung finden Sie auf https://www.max-it.de/managed-services/managed-client-services/
* '''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].


=== '''Kontakt''' ===
== Siehe auch ==
Wenn Sie Fragen oder Anmerkungen zu diesem Artikel haben, melden Sie sich bitte bei uns: mailto:techcorner@max-it.de.<br />
* [[SMTP]]
* [[WCF Rest-Service]]
* [[DSGVO]]


Über m.a.x. Informationstechnologie AG: <br />
== Links und Quellen ==
Als etabliertes Münchner Systemhaus zeichnen wir uns seit 1989 als verlässlicher IT-Partner mittelständischer und großer Unternehmen aus. Unser Portfolio reicht von IT- Services über individuelle Softwareentwicklung bis hin zur ERP-Beratung.<br />
* [https://learn.microsoft.com/de-de/dotnet/api/system.net.mail.smtpclient Microsoft Learn: SmtpClient-Klasse (mit Empfehlung für MailKit)]
=== '''Tags''' ===
* [https://www.max-it.de/development/individuelle-softwareentwicklung/ Individuelle Softwareentwicklung von m.a.x. it]
.Net, SmtpClient, MailMessage, archive, EML, store to disk


{{Uebermax}}
[[Kategorie:Softwareentwicklung]]


[[Kategorie: Server-Software]]

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