Parallel for Each VB or C# sending email issue with attachments
Hi Guys,
I am facing an issue with Parallel for each and adding attachments from a database. What appears to be happening is that the parralel for each starts before the attachment data has been created entirely thus causing the attachments to not have fully been written to memory.
So as an example you will find that if I was sending out 3 emails via parallel for each, the first 2 will have different file sizes (in term of the attachments) and only the last one will have the complete file?
Ok then here is the code that generates the attachments which are stored in a SQL Filetable:
TemplateAttachments is an ObservableCollection of another table in my application
Dim AttachmentFiles As Attachment Public Sub CreateAttachments() Dim fileData As Byte() = Nothing Dim filename As String = Nothing If TemplateAttachments.Count > 0 Then TemplateAttachments.ToList().ForEach(Function(i) Dim conn As New SqlConnection(ConfigurationManager.ConnectionStrings("MyConnectionString").ConnectionString) Dim strSQL As String = String.Format("SELECT stream_id, file_stream, name, path_locator, parent_path_locator, file_type, cached_file_size, creation_time, last_write_time, last_access_time, is_directory, is_offline, is_hidden, is_readonly, is_archive, is_system, is_temporary FROM dbo.MyFiles WHERE stream_id ='{0}'", i.stream_string_ID) Dim da As New SqlDataAdapter(strSQL, conn) Dim ds As New DataSet da.Fill(ds, "GetStream") fileData = DirectCast(ds.Tables("GetStream").Rows(0).Item(1), Byte()) filename = ds.Tables("GetStream").Rows(0).Item(2) Dim ms As New MemoryStream(fileData) ms.Seek(0, SeekOrigin.Begin) Dim contentType As New ContentType() With {.MediaType = MediaTypeNames.Application.Octet, .Name = filename} AttachmentFiles = New Attachment(ms, contentType) Return AttachmentFiles End Function) End If End Sub
THen the code for actually sending and the Async SMTP SendCompleted Event:
Public Sub SendMethod() IsSendDialogOpen = False IsCancelEnabled = False contacts = (From TblAddresses In _ctx.tblAddresses Where TblAddresses.list_ID = SelectedList.list_ID And TblAddresses.enabled = True And Not TblAddresses.email Is Nothing).ToList Dim subject As String = SelectedMessage.subject Dim body As String = "Hi there, This is a test" CreateAttachments() Dim po As New ParallelOptions() _cancelToken = New CancellationTokenSource() po.CancellationToken = _cancelToken.Token po.MaxDegreeOfParallelism = System.Environment.ProcessorCount * 2 Try Parallel.ForEach(contacts.Cast(Of Object), po, Function(row) Return SendEmail(row.email, subject, body) po.CancellationToken.ThrowIfCancellationRequested() End Function) Catch e As OperationCanceledException MessageBox.Show("Your submission was canceled") Finally _cancelToken.Cancel() _cancelToken.Dispose() _cancelToken = Nothing End Try IsSendEnabled = False IsFinishedEnabled = True AttachmentsReady = False End Sub Private Function SendEmail(recipient As String, subject As String, body As String) As Boolean Dim mm As New MailMessage(SelectedSMTP.smtp_sender_email, recipient) mm.Subject = subject mm.IsBodyHtml = True mm.Body = body If IsBCC = True Then mm.Bcc.Add(SelectedSMTP.smtp_sender_email) End If If AttachmentFiles IsNot Nothing Then mm.Attachments.Add(AttachmentFiles) End If Dim smtp As New SmtpClient() smtp.Host = smtp_name 'smtp.EnableSsl = True Dim state As [Object] = mm AddHandler smtp.SendCompleted, AddressOf smtpClient_SendCompleted Try smtp.SendAsync(mm, state) Catch ex As Exception MsgBox(ex.ToString) End Try Return True End Function Private Sub smtpClient_SendCompleted(sender As Object, e As System.ComponentModel.AsyncCompletedEventArgs) Dim token As MailMessage = TryCast(e.UserState, MailMessage) If e.Cancelled Then Exit Sub End If If e.Error IsNot Nothing Then Application.Current.Dispatcher.BeginInvoke(DirectCast(Sub() SteppedCollection.Add(New FailedContactList() With { .FailedEmail = token.To.ToString & " (" & e.Error.Message & ")" }), Action)) TotalFailed = TotalFailed + 1 Else TotalSent = TotalSent + 1 End If End SubEnd Class
I appreciate that there are variables that you may not get or understand,however the crux of my problem is not that but more of how to ensure that my attachments have been fully loaded prior to starting the parallel for each?