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

9

我目前正在研究在C#中对MailMessage对象进行序列化。虽然网络上有几种示例,但它们将序列化为二进制,这有点违背了我的期望。

我的想法是将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并添加一个序列化函数,该函数会自动返回rfc822字符串而无需将其放在文件系统中。
由于我似乎无法使用Visual Studio跟踪到SMTPClient.Send函数,所以这种“理想”方式有些棘手。
我已经通过对Peter Huber的POP3MimeClient和POP3MailClient类进行一些小修改来解析RFC消息,因此我可以将硬盘驱动器上的文件或字符串反序列化回MailMessage。
让我烦恼的是,我真的应该能够使用流将MailMessage进行序列化,并将流写入自己选择的字符串或文件中,而不是像上面的代码那样绕道创建基于GUID的文件夹,并将文件内容读回字符串中...
如果您能指出如何使此更加优雅的方法,我将不胜感激。
谢谢。

嗨@PaulV - 你介意分享反序列化部分吗?非常感谢。 - YS.
1
嗨,很难分享。当时我正在合同工作,现在我已经无法访问代码了。如果我没记错的话,它基本上是将字符串从数据库加载到流中,然后从那里填充消息对象,就像你从流上下载它一样,就像从POP服务器上下载一样。 - PaulV
2个回答

21

这将是一种黑客方式。不要忘记,微软可能会在下一个版本的.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();

1
谢谢。我稍微调整了一下,以适应我们的自定义MailMessage对象,该对象具有一些额外的功能,结果非常出色。它完美地工作,并且快速而优雅,正如我认为应该是可能的那样。非常酷:D - PaulV
3
为什么他们不提供公共API呢! - John Leidegren
3
这是一个非常好的扩展。是否有人实现了它的倒数,即将EML转换回MailMessage? - TheEdge
1
使用POP客户端并覆盖一个或两个函数,将流加载到MailMessage中。RFC2822格式是在POP和IMAP客户端上提供的相同格式,因此您应该能够轻松地对其进行反序列化。 - PaulV
可以添加using()来显式地处理MemoryStream,也可以在MailWriter上调用Close:http://stackoverflow.com/questions/25897922/how-invoke-mailwriter-through-reflection-could-corrupt-file - ofthelit
显示剩余3条评论

0

经过很多努力,我终于成功反序列化了MailMessage:

为了做到这一点,我创建了一个dll,将在客户端应用程序和Web服务中使用。这需要是相同的才能进行反序列化 :)

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

非常感谢您的分享!

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

所以我这样转换MailMessage:string serializedMessage = SerializeAsBase64(mail); 在Web服务中,我这样重构它: MailMessage Mm = DeserializeFromBase64(serializedMessage);

希望这有所帮助...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接