Элегантный способ сериализации объекта MailMessage в .NET

В настоящее время я рассматриваю сериализацию объекта MailMessage на С#, и, хотя в сети есть несколько вариантов примера, они сериализуются в двоичный код, что, по-моему, не соответствует сути.

Мой подход заключается в том, что я хотел бы сериализовать MailMessage в строку eml RFC2822, и приведенный ниже код - это то, что я придумал.

    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, даже не затрагивая файловую систему.

Поскольку мне кажется, что я не могу отследить функцию SMTPClient.Send с помощью Visual Studio, этот «идеальный» способ ведения дел немного сложен.

Я уже разобрался с десериализацией сообщения RFC, добавив небольшие изменения в классы POP3MimeClient и POP3MailClient Питера Хубера, чтобы я мог десериализовать файл на жестком диске или строку обратно в MailMessage.

Меня раздражает то, что я действительно должен иметь возможность сериализовать MailMessaage, используя поток, и записывать поток в строку или файл по моему выбору вместо того, чтобы ходить по домам и создавать папки на основе GUID, как это делает приведенный выше код. и чтение содержимого файла обратно в строку...

Любые указатели на то, как я могу сделать это более элегантным, будут высоко оценены.

Спасибо.


person PaulV    schedule 11.09.2013    source источник
comment
привет @PaulV - не могли бы вы поделиться битом десериализации, пожалуйста? Очень признателен.   -  person YS.    schedule 28.04.2015
comment
Привет, это не просто поделиться. В то время я работал над контрактом, и у меня больше нет доступа к коду. Если я правильно помню, это была в значительной степени загрузка строки из БД в поток, а затем заполнение объекта сообщения оттуда, как если бы вы загрузили его с POP-сервера в потоке.   -  person PaulV    schedule 11.05.2015


Ответы (2)


Это будет взлом. Не забывайте, 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();
person I4V    schedule 11.09.2013
comment
Спасибо за это. Я немного изменил его, чтобы он справился с нашим пользовательским объектом MailMessage, который имеет некоторые дополнительные функции, и результаты впечатляют. Он работает отлично, быстро и элегантно, как я и думал. Очень круто :D - person PaulV; 12.09.2013
comment
Почему они не делают публичные API! - person John Leidegren; 24.04.2014
comment
Тупость с моей стороны, удалять комментарий сейчас. - person muttley91; 16.07.2014
comment
Это действительно хорошее расширение. Кто-нибудь реализовал обратную связь, а именно EML, обратно в MailMessage? - person TheEdge; 10.09.2014
comment
Используйте клиент POP и переопределите одну или две функции, чтобы взять поток и загрузить его в MailMessage. Формат RFC2822 — это тот же формат, который обслуживается клиентами POP и IMAP, поэтому вы сможете легко его десериализовать. - person PaulV; 11.05.2015
comment
Можно добавить using() для явного удаления MemoryStream, а Close можно вызвать для MailWriter: can-corrupt-file" title="как вызов mailwriter через отражение может повредить файл"> stackoverflow.com/questions/25897922/ - person ofthelit; 18.05.2015
comment
Может ли это работать и для пустого поля "От" в MailMessage? - person Richard; 22.03.2016
comment
Привет. Пока не углубляясь, будет ли вышеописанное работать с VS2005 и .net2? - person Fandango68; 16.11.2016

Наконец-то мне удалось десериализовать MailMessage после большой работы:

Чтобы все было правильно, я создал dll, которая будет использоваться как в клиентском приложении, так и в веб-сервисе. Это должно быть то же самое, чтобы десериализация работала :)

Я выбрал классы сериализации dll здесь: https://bitbucket.org/burningice/compositec1contrib.email/src/0bba07df532c8134717cfb40757f4cb22f002b1d/Email/Serialization/?at=default

Большое спасибо, что поделились!

После компиляции и добавления моей новой dll в проекты отправка и получение работали.

Итак, я конвертирую MailMessage следующим образом: string serializedMessage = SerializeAsBase64(mail); и в веб-сервисе я реконструирую его следующим образом: MailMessage Mm = DeserializeFromBase64(serializedMessage);

Надеюсь, это поможет...

person Kemboi    schedule 13.03.2018