Ошибка при отправке массового электронного письма: «Асинхронный вызов уже выполняется. Он должен быть завершен или отменен, прежде чем вы сможете вызвать этот метод» - PullRequest
0 голосов
/ 26 апреля 2018

Я создал одно консольное приложение для переноса данных из одной таблицы базы данных в другую, в которой переносятся записи о клиентах, поэтому я должен уведомить их об изменении пароля.

public static async Task<bool> SendRegisterEmail(List<MailMessage> mailMessage)
{
    bool flag = true;
    try
    {
        var smtp = new SmtpClient();
        var taskEmails = mailMessage.Select(x => smtp.SendMailAsync(x));

        await Task.WhenAll(taskEmails); // **Error : An asynchronous call is already in progress. It must be completed or canceled before you can call this method**
    }
    catch (Exception ex)
    {
        throw ex;
    }

    return flag;
}

электронные письма успешно отправляются асинхронно, если я удаляю это await Task.WhenAll (taskEmails) , но когда операция миграции завершается, он не отправляет все электронные письма. многие из них остаются не отправленными в консольном приложении, закрытом после завершения операции, и у меня есть более 1 000 000 записей, так как я могу продолжить процесс отправки электронной почты в фоновом режиме или приложение работает до тех пор, пока все электронные письма не будут успешно отправлены?

Вот код переноса данных:

foreach (DataRow SourceReader in DS.Tables[0].Rows)
{
    insertCounter++;
    using (SqlCommand DestinationCommand = DestinationConnection.CreateCommand())
    {
        Console.WriteLine("inserting row...");
        var date = (SourceReader["LockedUntil"] == DBNull.Value ? Convert.ToDateTime("01/01/1753") : Convert.ToDateTime(SourceReader["LockedUntil"]));
        DestinationCommand.CommandText = string.Format(insertQuery1, (SourceReader["CustomerGUID"] == DBNull.Value ? null : SourceReader["CustomerGUID"].ToString()), (SourceReader["FirstName"] == DBNull.Value ? null : SourceReader["FirstName"].ToString()), (SourceReader["LastName"] == DBNull.Value ? null : SourceReader["LastName"].ToString()), (SourceReader["Email"] == DBNull.Value ? null : SourceReader["Email"].ToString()), 1, PasswordManager.Encrypt(SourceReader["FirstName"].ToString() + "1!"), (SourceReader["Phone"] == DBNull.Value ? null : SourceReader["Phone"].ToString()), 1, 0, 0, date.ToString("yyyy-MM-ddTHH:mm:ss"), Convert.ToInt16(SourceReader["BadLoginCount"] == DBNull.Value ? 0 : SourceReader["BadLoginCount"]), Convert.ToInt16(SourceReader["OkToEmail"] == DBNull.Value ? 0 : SourceReader["OkToEmail"]), (SourceReader["CustomerGUID"] == DBNull.Value ? null : SourceReader["CustomerGUID"].ToString()), 2);
        DestinationCommand.ExecuteNonQuery();
        Console.WriteLine("AspNetUser Row inserted...!!! ");
    }
    if ((insertCounter % 100) == emailCounter)
    {
        var message = Email.AddUserForEmail(new User() { Email = (SourceReader["Email"] == DBNull.Value ? null : SourceReader["Email"].ToString()), Password = (SourceReader["FirstName"].ToString() + "1!"), FirstName = (SourceReader["FirstName"] == DBNull.Value ? null : SourceReader["FirstName"].ToString()), LastName = (SourceReader["LastName"] == DBNull.Value ? null : SourceReader["LastName"].ToString()) });
        mailList.Add(message);
        var flag = Email.SendRegisterEmail(mailList);
        emailCounter++;
    }
    else if (insertCounter == totalCount)
    {
        var message = Email.AddUserForEmail(new User() { Email = (SourceReader["Email"] == DBNull.Value ? null : SourceReader["Email"].ToString()), Password = (SourceReader["FirstName"].ToString() + "1!"), FirstName = (SourceReader["FirstName"] == DBNull.Value ? null : SourceReader["FirstName"].ToString()), LastName = (SourceReader["LastName"] == DBNull.Value ? null : SourceReader["LastName"].ToString()) });
        mailList.Add(message);
        var flag = Email.SendRegisterEmail(mailList);
    }
    else
    {
        var message = Email.AddUserForEmail(new User() { Email = (SourceReader["Email"] == DBNull.Value ? null : SourceReader["Email"].ToString()), Password = (SourceReader["FirstName"].ToString() + "1!"), FirstName = (SourceReader["FirstName"] == DBNull.Value ? null : SourceReader["FirstName"].ToString()), LastName = (SourceReader["LastName"] == DBNull.Value ? null : SourceReader["LastName"].ToString()) });
        mailList.Add(message);
    }
    // var i = Email.SendRegisterEmail((SourceReader["Email"] == DBNull.Value ? null : SourceReader["Email"].ToString()), (SourceReader["FirstName"].ToString() + "1!"), (SourceReader["FirstName"] == DBNull.Value ? null : SourceReader["FirstName"].ToString()), (SourceReader["LastName"] == DBNull.Value ? null : SourceReader["LastName"].ToString()));
}

1 Ответ

0 голосов
/ 26 апреля 2018

SmtpClient не позволяет вам выполнять несколько асинхронных операций одновременно, это то, что сообщает вам сообщение об ошибке.Вы делаете это с:

var smtp = new SmtpClient();
var taskEmails = mailMessage.Select(x => smtp.SendMailAsync(x));
await Task.WhenAll(taskEmails);

Вы также не утилизируете SmtpClient, что тоже не помогает.

Вместо этого, либо отправьте их по одному:

using (var smtp = new SmtpClient()){
    foreach (var email in mailMessage) {
        await smtp.SendMailAsync(email);
    }
}

Или используйте отдельный SmtpClient для каждой отправки:

Func<MailMessage, Task> sendFunc = async (x) => {
    using (var smtp = new SmtpClient()) {
        await smtp.SendMailAsync(x);
    }
};
var taskEmails = mailMessage.Select(sendFunc);
await Task.WhenAll(taskEmails);

Похоже, что вы также не ожидаете отправки из функции миграции:

// flag is Task<bool> here
var flag = Email.SendRegisterEmail(mailList);

Если этоне опечатка - вам нужно ждать их на месте, или собирать задачи в некотором списке и ждать их после цикла все вместе (с await Task.WhenAll).

Обратите внимание, что если вы отправляете много писемособенно в том же домене, особенно параллельно - используемый вами SMTP-сервер (или SMTP-сервер получателя) может быть не очень доволен этим и может на некоторое время вывести вас в черный список.

Лучше использовать отдельный фоновый процесскоторый будет отправлять ваши электронные письма, совершенно не связанные с вашей процедурой миграции.Просто позвольте процедуре миграции вставить информацию об ожидающих сообщениях в какое-либо постоянное хранилище (таблицу базы данных), а затем разрешите другому приложению исследовать эту таблицу и отправлять электронные письма.Таким образом, процедура переноса не прерывается при сбое электронной почты, и фоновый процесс, который отправляет электронную почту, может при необходимости повторить их.

...