在 .NET 中序列化 MailMessage 对象的优雅方式

Posted

技术标签:

【中文标题】在 .NET 中序列化 MailMessage 对象的优雅方式【英文标题】:Elegant way to serialize a MailMessage object in .NET 【发布时间】:2013-09-15 17:25:07 【问题描述】:

我目前正在研究在 C# 中序列化 MailMessage 对象,尽管网络上有几个示例的变体,但它们序列化为二进制,这与 IMO 的重点无关。

我的方法是我想将 MailMessage 序列化为 RFC2822 eml 字符串,下面的代码就是我想出的。

    public string SerializeEmail(MailMessageArgs e)
    
        string rfc822eml = "";
        Guid g = Guid.NewGuid();
        lock (g.ToString())
        
            System.IO.DirectoryInfo di = new System.IO.DirectoryInfo(@"C:\tmpspool");
            di.CreateSubdirectory(g.ToString());
            string spoolDir = @"C:\tmpspool\" + g.ToString();
            SmtpClient Client = new SmtpClient("localhost");
            Client.DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory;
            Client.PickupDirectoryLocation = spoolDir;
            Client.Send(e.mailObj);
            var files = from file in Directory.EnumerateFiles(spoolDir)
                       select file;
            string serializedEml = files.First();
            rfc822eml = File.ReadAllText(serializedEml);
            File.Delete(serializedEml);
            Directory.Delete(spoolDir);
        

        return rfc822eml;
    

这很糟糕,但它确实有效。不过,理想情况下,我会创建一个新的 SMTPClient 并添加一个 Serialize 函数,该函数会自动返回 rfc822 字符串,而不会碰到文件系统。

由于我似乎无法使用 Visual Studio 跟踪 SMTPClient.Send 函数,因此这种“理想”的处理方式有点棘手。

我已经通过对 Peter Huber 的 POP3MimeClient 和 POP3MailClient 类添加一些小的更改来整理 RFC 消息的反序列化,以便我可以将 HDD 上的文件或字符串反序列化回 MailMessage。

困扰我的是,我真的应该能够使用流序列化 MailMessaage 并将流写入我选择的字符串或文件,而不是四处走动创建基于 GUID 的文件夹作为上面的代码执行并将文件的内容读回字符串...

任何关于我如何使它更优雅的指针将不胜感激。

谢谢。

【问题讨论】:

嗨@PaulV - 你介意分享反序列化位吗?非常感谢。 您好,不易分享。我当时正在签订一份合同,我不再有权访问代码。如果我没记错的话,它几乎是将字符串从数据库加载到流中,然后从那里填充消息对象,就像您从流中的 POP 服务器下载它一样。 【参考方案1】:

这将是一个 hack。别忘了,MS 可以在 .Net 的下一个版本中更改它。

(在 ILSpy 的帮助下)您可以编写如下扩展方法

public static class MailExtensions

    public static string ToEml(this MailMessage mail)
    
        var stream = new MemoryStream();
        var mailWriterType = mail.GetType().Assembly.GetType("System.Net.Mail.MailWriter");
        var mailWriter = Activator.CreateInstance(
                            type: mailWriterType,
                            bindingAttr: BindingFlags.Instance | BindingFlags.NonPublic,
                            binder: null,
                            args: new object[]  stream ,
                            culture: null,
                            activationAttributes: null);

        mail.GetType().InvokeMember(
                            name: "Send",
                            invokeAttr: BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod,
                            binder: null,
                            target: mail,
                            args: new object[]  mailWriter, true, true );


        return Encoding.UTF8.GetString(stream.ToArray());
    

并用作

string eml = mailMessage.ToEml();

【讨论】:

谢谢。我已经稍微调整了它以应对我们的自定义 MailMessage 对象,该对象具有一些额外的功能,结果令人印象深刻。正如我认为的那样,它完美地工作并且快速而优雅。非常酷:D 为什么他们不公开 API! 我很愚蠢,现在删除评论。 这是一个非常好的扩展。有没有人实现了倒数,即 EML 回到 MailMessage? 使用 POP 客户端并重写一两个函数来获取流并将其加载到 MailMessage 中。 RFC2822 格式与通过 POP 和 IMAP 客户端提供的格式相同,因此您应该能够很容易地对其进行反序列化。【参考方案2】:

经过大量工作,我终于设法反序列化 MailMessage:

为了做到这一点,我创建了一个 dll,它将在客户端应用程序和 Web 服务中使用。这必须是相同的反序列化工作:)

我在这里选择了 dll 序列化类:https://bitbucket.org/burningice/compositec1contrib.email/src/0bba07df532c8134717cfb40757f4cb22f002b1d/Email/Serialization/?at=default

非常感谢分享!

一旦编译并将我的新 dll 添加到项目中,发送和接收就可以工作了。

所以我以这种方式转换 MailMessage:string serializedMessage = SerializeAsBase64(mail); 在Webservice中,我以这种方式重建它: MailMessage Mm = DeserializeFromBase64(serializedMessage);

希望对你有帮助……

【讨论】:

以上是关于在 .NET 中序列化 MailMessage 对象的优雅方式的主要内容,如果未能解决你的问题,请参考以下文章

如何序列化邮件消息?

在 .net 核心中将 MailMessage 转换为 MemoryStream

将 System.Net.mail.MailMessage 保存为 .msg 文件

将 System.Net.Mail.MailMessage 替换为手动创建的消息并发送

可以在 C# 中的委托上传递 System.Net.Mail.MailMessage 对象吗?

为啥我需要 Dispose 一个 System.Net.Mail.MailMessage 实例?