Solved

Parallel for Each VB or C# sending email issue with attachments

Posted on 2016-08-31
1
114 Views
Last Modified: 2016-09-12
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

Open in new window


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 Sub


End Class

Open in new window


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?

Solutions in  C# are more than welcome.

Thank you in advance
0
Comment
Question by:databarracks
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
1 Comment
 
LVL 11

Accepted Solution

by:
louisfr earned 500 total points
ID: 41786483
The ForEach call on TemplateAttachments writes in the AttachmentFiles variable each loop.
You lose all but the last result.
0

Featured Post

Free Tool: IP Lookup

Get more info about an IP address or domain name, such as organization, abuse contacts and geolocation.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

For those of you who don't follow the news, or just happen to live under rocks, Microsoft Research released a beta SDK (http://www.microsoft.com/en-us/download/details.aspx?id=27876) for the Xbox 360 Kinect. If you don't know what a Kinect is (http:…
Parsing a CSV file is a task that we are confronted with regularly, and although there are a vast number of means to do this, as a newbie, the field can be confusing and the tools can seem complex. A simple solution to parsing a customized CSV fi…
In an interesting question (https://www.experts-exchange.com/questions/29008360/) here at Experts Exchange, a member asked how to split a single image into multiple images. The primary usage for this is to place many photographs on a flatbed scanner…
How to Install VMware Tools in Red Hat Enterprise Linux 6.4 (RHEL 6.4) Step-by-Step Tutorial

751 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question