vb.netemaildisposesendasync

How do I dispose System.Net.Mail.MailMessage in VB when using SendAsync


I have not managed to find a conclusive answer for this on here or in MSDN. When using the following code, if I try to dispose the message after sending the asynch mail then I get a System.ObjectDisposedException: Cannot access a disposed object. This is when the microsoft example suggests disposing. I can't dispose the message in the callback because it is out of scope. I have tried using a module level Mail message but get the same results. If I don't dispose the message everything works fine but this is not good practice. Please advice the best place to dispose the mail message.

 Public Sub SendEmailWithReport(ByVal v_objEmailAddress As System.Collections.Generic.List(Of String), ByVal v_strSubject As String,
   ByVal v_strBody As String, ByVal v_strFileName As String, ByVal v_intContentID As Integer,
   ByVal v_strType As String) Implements IMessagingPlatform.SendEmailWithReport

    Dim objMessage As New MailMessage
    Dim objAttachment As New Attachment(v_strFileName, MediaTypeNames.Application.Pdf)

    Try
        'configure e-mail addresses and add attachment
        With objMessage
            For Each strAddress As String In v_objEmailAddress
                .To.Add(strAddress)
            Next
            .Attachments.Add(objAttachment)
        End With

        SetUpAsynchEmail(objMessage, v_strSubject, v_strBody, v_strFileName, v_intContentID, v_strType)

    Catch ex As Exception
        Trace.WriteLine(DateTime.Now().ToString("dd/MM/yyyy HH:mm:ss.fff") & " : Failed to setup e-mail with report: " & ex.Message.ToString, "ERR")
    Finally
        'objMessage.Dispose() *disposing here gives System.ObjectDisposedException: Cannot access a disposed object.**
        'objAttachment.Dispose()
    End Try
End Sub

Private Sub SetUpAsynchEmail(ByVal v_objMessage As MailMessage, ByVal    v_strSubject As String, ByVal v_strBody As String,
                              ByVal v_strFileName As String, ByVal v_intContentID As Integer, ByVal v_strType As String)
    Dim intID As Integer
    Try
        Dim basicAuthenticationInfo As New System.Net.NetworkCredential(mstrUserName, mstrPassword)
        Dim objClient As New SmtpClient()

        'configure mail message
        With v_objMessage
            .IsBodyHtml = True
            .Subject = v_strSubject
            .Body = v_strBody

            If mstrFromEmailName <> "" Then
                .From = New MailAddress(mstrFromEmailAddress, mstrFromEmailName)
            Else
                .From = New MailAddress(mstrFromEmailAddress)
            End If
        End With

        'configure mail client
        With objClient
            .Host = mstrSMTPHost
            .UseDefaultCredentials = False
            .Credentials = basicAuthenticationInfo
            .EnableSsl = True
            .Port = mintSMTPPort
        End With

        ' Set the method that is called back when the send operation ends. 
        AddHandler objClient.SendCompleted, AddressOf SendCompletedCallback
        'Generate a unique message number
        intID = mobjContainer.AddUnsentMail(v_intContentID, v_strType, v_strFileName)
        If intID > -1 Then
            objClient.SendAsync(v_objMessage, intID)
        End If

        'v_objMessage.Dispose() **disposing here gives System.ObjectDisposedException: Cannot access a disposed object.

    Catch ex As Exception
        Trace.WriteLine(DateTime.Now().ToString("dd/MM/yyyy HH:mm:ss.fff") & " : Failed to setup e-mail: " & ex.Message.ToString, "ERR")
    Finally

    End Try
End Sub

Private Sub SendCompletedCallback(ByVal v_objSender As SmtpClient, ByVal e As AsyncCompletedEventArgs)
    ' Get the unique identifier for this asynchronous operation. 
    Dim strMessageID As String = CStr(e.UserState)

    Try
        If e.Cancelled Then
            Trace.WriteLine(DateTime.Now().ToString("dd/MM/yyyy HH:mm:ss.fff") & " : E-mail cancelled for message with ID " &
                            strMessageID, "ERR")
        End If
        If e.Error IsNot Nothing Then
            Trace.WriteLine(DateTime.Now().ToString("dd/MM/yyyy HH:mm:ss.fff") & " : Failed to send e-mail with ID " &
                            strMessageID & ", " & e.Error.ToString(), "ERR")
            'E-mail error - update table
            mobjContainer.UpdateUnsentMail(CInt(strMessageID))
        Else
            'E-mail success - delete record from table
            mobjContainer.DeleteUnsentMailItem(CInt(strMessageID))
            Trace.WriteLineIf(mobjLogTrace.LogEvents = True And mobjLogTrace.LogDetail >= 4, DateTime.Now().ToString("dd/MM/yyyy HH:mm:ss.fff") &
                              " : E-mail with ID " & strMessageID & " successfully sent", "EVT")
        End If

    Catch objException As Exception
        Trace.WriteLine(DateTime.Now().ToString("dd/MM/yyyy HH:mm:ss.fff") & " : Failed to send e-mail with ID " &
                           strMessageID & ", " & objException.ToString(), "ERR")
    Finally
        v_objSender.Dispose()
    End Try
End Sub

Solution

  • If you created a class to hold both intID and your v_objMessage and passed this as the second parameter in your objClient.SendAsync() call you would have access to both the ID and the message in the SendCompletedCallback() sub. You could then call .Dispose on the message in your Finally block.