SmtpClient API documentation for Aspose.Email 21.5

We are running into an issue with the Message ID header being set incorrectly in version 18.6 and so are looking to upgrade to the latest Aspose.Email library, which appears to be 21.5. It appears, however, that APIs have changed and we are not seeing clear documentation about how the new API structure is supposed to be used. Specifically, BeginSend/EndSend no longer exist in 21.5. There is a SendAsync, but there isn’t a clear way to use a CancellationToken with this method. The ConnectionTimeout property also no longer appears to be available and it isn’t clear what the appropriate substitute is supposed to be.

Note that the API documentation on the Web site (Class SmtpClient | Aspose.Email for .NET API Reference) still references version 21.3 and talks about BeginSend/EndSend, but those methods don’t appear to exist in 21.5. Similarly, I found a .chm file in the Help subdirectory of my NuGet package install that seems to reference version 21.1. I also looked in the release notes and can’t find any indication of such breaking changes to the API. Where should we expect to find documentation of breaking changes of this kind?

@kadhim,
Welcome to our community! Thank you for the query. I will answer you later as soon as possible.

@kadhim,

I added a ticket with ID EMAILNET-40256 to our tracking system. Our development team will prepare information for you. I will inform you of any news.

Thank you for the comment. This API reference will be updated later.

You are right. Information about these changes and the new API should be in Release Notes 21.1. We apologize for any inconvenience caused.

@kadhim,
The BeginSend and EndSend methods were deprecated in February 2019. And then they were removed from the API for net4.5 and netstandard in January 2020, as there is async/await. These methods are still relevant for versions net2.0, net3.5, net4.0.

I’m not sure that I understand. I looked through your release notes from 2019 and early 2020 and don’t see any mention of deprecation or removal of those APIs. Can you point me to documentation of the deprecation and removal? Does Aspose have a stated policy regarding the timeline and communication of API changes and removals? It is suprising that all of the documentation that I can find still references the BeginSend/EndSend pattern more than 2 years after you indicate that the API was deprecated.

We are also interested to know how to handle what was formerly the ConnectionTimeout property.

Do you have an estimated timeline for providing updated documentation?

@kadhim,
As I mentioned above, this information wasn’t published. It is our mistake.

I will inform you when our development team prepares the information.

I requested an estimated time to fix the documentation. I will let you know as soon as possible.

@kadhim,
You should use the SendAsync method instead of BeginSend /EndSend methods. The GreetingTimeout property is now used instead of the ConnectionTimeout property. The new property is used only for waiting for a greeting answer from a server when a connection is established. The operation of canceling is still under discussion for the new API.
The documentation will be updated later. We apologize for any inconvenience caused.

It appears that the GreetingTimeout property was also removed in 21.5 (it was there in 21.3) so I’m not sure what alternative should be used. Also, does the duration of GreetingTimeout include the time taken to make the connection as well as receive the greeting or is it only the time after the connection is made and the greeting is sent? The latter would make it far less useful for this purpose. Does CancelAsyncOperation still work with SendAsync?

@kadhim,
Thank you for your questions. I will answer you as soon as possible.

@kadhim,
I checked and found that the GreetingTimeout property is there for all versions of .NET in Aspose.Email 21.5. Could please clarify what you mean?

Also, you can use Timeout property only. GreetingTimeout property is used when there are problems with connections. I passed your remaining questions to our development team.

Sorry. I had misremembered. Yes - the GreetingTimeout property is there. So I gather from your reply that the GreetingTimeout starts from the time when the connection is initiated so that it includes any time taken to make the connection.

@kadhim,
I am still waiting for detailed information for you from our development team. I will answer you as soon as possible.

@kadhim,
Email clients may execute quite long operations. To limit the time of operations, you have to use EmailClient.Timeout property. Values for this property have to be long intervals so as to not prevent long-time operations. But in some cases, if EmailClient uses only Timeout property, connection establishing may take a long time. For example, the mail client may use the automatic mode to connection establishing. In this mode, the email client goes through all possible connection parameters step by step till the connection is established. SMTP, IMAP and POP3 servers send a greeting string to the client in case of correct connection establishing. Servers may use implicit or explicit (START TLS) SSL/TLS connection initiation. In case of connection mode is mismatched (for example, the server is waiting for an implicit SSL connection but the client tries to establish a non-secured or explicit SSL connection) server won’t send a greeting string. In this case, you will wait a long time until the Timeout will be reached in awaiting for a greeting string, and the client goes to the next connection option. To avoid this problem, the GreetingTimeout property has been introduced. This property allows you to set the timeout for the greeting string and reduce the time of automatic connection establishment.

GreetingTimeout is only the time after the connection is made and the greeting is sent. If we only use the common timeout for connection operation (without GreetingTimeout), it may lead (in some cases) to all connection timeout will be spent waiting for the greeting string.

CancelAsyncOperation method does not work with SendAsync method.

I certainly understand the reason for having a separate timeout for making the connection - you previously had a property called ConnectionTimeout for this purpose that appears to have been removed and later GreetingTimeout was added. If I understand correctly, GreetingTimeout includes both the time taken to make the connection as well as the time to receive a greeting from the server, which is what I was looking to confirm.

If CancelAsyncOperation does not work with SendAsync, what is its purpose?

@kadhim,
CancelAsyncOperation method works with BeginSend / EndSend methods. Operation of cancelation for the SendAsync method will be implemented within the task with ID EMAILNET-40256 linked to this thread.

But BeginSend and EndSend do not exist any more (we would have preferred if you had kept them), so why is CancelAsyncOperation still there?

@kadhim,
Thank you for your comment. This method will be removed for .NET Framework 4.5 and .NET Standard 2.0 versions as well.

The issues you have found earlier (filed as EMAILNET-40256) have been fixed in this update.

@kadhim,
The code snippet below shows how to cancel the operation in the TAP model with Aspose.Email 21.7 just for demonstration. This cancellation mechanism is acceptable for all methods in all email clients (IMAP/POP3/SMTP). For other clients and methods code will be similar.

List<MailMessage> messages = new List<MailMessage>();
Guid guid = Guid.NewGuid();

for (int i = 0; i < 100; i++)
    messages.Add(new MailMessage(
        "<from>", "<to>", $"Subject - {guid} - {i}", "Email body"));

using (ImapClient client = new ImapClient("host", 143, "username", "password"))
{
    CancellationTokenSource tokenSource = new CancellationTokenSource();
    AppendMessagesResult appendResult = null;
    Exception exception = null;
    AutoResetEvent autoResetEvent = new AutoResetEvent(false);

    ThreadPool.QueueUserWorkItem(delegate (object state)
    {
        try
        {
            appendResult = client.AppendMessagesAsync(messages, tokenSource.Token).GetAwaiter().GetResult();
        }
        catch (Exception ex)
        {
            exception = ex;
        }
        finally
        {
            autoResetEvent.Set();
        }
    });
    Thread.Sleep(2000);
    tokenSource.Cancel();
    autoResetEvent.WaitOne();
    Assert.IsTrue(exception is OperationCanceledException);
}

I see. It does not appear that online documentation has been updated to reflect this. The API reference documentation still references version 21.6 and does not appear to make reference to this code pattern.