• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1051
  • Last Modified:

Sending mass email using a backgroundworker

I have a program that pulls up a client list and creates a mass email to certain clients.  The user can choose to send the email directly from my software to create a mail merge in microsoft word.  If they choose to send the email from my software, everything works properly, but it takes a substantial amount of time for the emails to send.  During that time, the software is frozen waiting for all emails to be sent.

I haven't really used the backgroundworker, but I assume this is a scenario I would need to use it.  I posted my code below, and I have already tried to cut and paste it into the backgroundworker.dowork event, but the code fails for referencing items outside of the thread.

The section below "Prepare Emails" reads the client list from a datagrid, fills the email with the clients name and email address, sends the email, then loops to the next client.

Can someone direct me on how to convert that section into a backgroundworker process so that my user can continue using the software while the emails send?


Me.Cursor = Cursors.AppStarting

        'set variables for company information
        Dim borrowersrow As PlannerFilesDataSet.CompanytableRow
        'create today's date variable
        Dim tdate As Date
        'find installed directory of program so that csv file can be saved
        Dim appPath As String = Path.GetDirectoryName(Application.ExecutablePath)
        'create variable for current client status and add value
        Dim emailstatus As String
        'save email count value
        Dim emailcount As Integer
        'save the email template if sending directly from software
        Dim emailtosend As String = ""
        Dim emailtemplate As String = ""
        Dim sDBPath As String
        Dim foundtag As Integer = 0
        Dim tag As String = ""
        Dim i As Integer = 0
        Dim p As Integer
        Dim emailrow As PlannerFilesDataSet.BorrowersRow
        Dim addedrowcount As Integer = 0
   

        'create email account variables
        Dim emailaddress As String = ""
        Dim emailpass As String = ""
        Dim emailport As String = ""
        Dim emailhost As String = ""
        Dim emailssl As String = ""

        Try

            'PREPARE DATAGRID-------------------------------------------------------------------

            GroupBox3.Visible = False
            GroupBox4.Visible = True

            'fill variables
            borrowersrow = Main.PlannerFilesDataSet.Companytable.Rows(0)
            tdate = Today.Date
            emailstatus = cboMailMergeStatus.Text
            'save email account variables
            emailaddress = borrowersrow.Item("setemailaddress").ToString()
            emailpass = borrowersrow.Item("setemailpassword").ToString()
            emailport = borrowersrow.Item("setoutgoingport").ToString()
            emailhost = borrowersrow.Item("setoutgoingserver").ToString()
            If borrowersrow.Item("setemailusessl").ToString() = "Yes" Then
                emailssl = "True"
            Else
                emailssl = "False"
            End If

            Main.BorrowersBindingSource.Filter = Nothing

            'set variables to delete empty rows

            p = Main.DataGridLoad.Rows.Count - 1

            'remove blank rows from the datagrid
            For i = 0 To p
                If i <= p Then
                    If Convert.ToString(Main.DataGridLoad.Rows(i).Cells(0).Value) = "" And Convert.ToString(Main.DataGridLoad.Rows(i).Cells(1).Value) = "" And Convert.ToString(Main.DataGridLoad.Rows(i).Cells(2).Value) = "" And Convert.ToString(Main.DataGridLoad.Rows(i).Cells(3).Value) = "" And Convert.ToString(Main.DataGridLoad.Rows(i).Cells(4).Value) = "" And Convert.ToString(Main.DataGridLoad.Rows(i).Cells(5).Value) = "" Then
                        Main.DataGridLoad.Rows.RemoveAt(i)
                        i = i - 1
                        p = Main.DataGridLoad.Rows.Count - 1
                    End If
                End If
            Next

            'sort datelastemailed by oldest to be emailed first
            Main.GridMailMerge.Sort(Main.DateLastEmailedDataGridViewTextBoxColumn, ListSortDirection.Ascending)

            'Check the email total borrowers radio buttons
            If rdo50.Checked = True Then
                emailcount = 50
            ElseIf rdo100.Checked = True Then
                emailcount = 100
            ElseIf rdo150.Checked = True Then
                emailcount = 150
            End If

            'set counter for datagridview looping
            Dim loannumbersemailed(emailcount)

            'filter the datagridview by lead status
            Main.BorrowersBindingSource.Filter = String.Format("Status Like '" & emailstatus & "*'" & " AND " & "Bemail Like '%@%'")

            'CREATE CSV FILE AND COUNT LEADS AVAILABLE FOR EMAIL---------------------------

            If Main.GridMailMerge.Rows.Count > 1 Then

                'begin to create the csv file from the current datagridview
                Dim headers = (From header As DataGridViewColumn In Main.GridMailMerge.Columns.Cast(Of DataGridViewColumn)() Select header.HeaderText).ToArray
                Dim rows = From row As DataGridViewRow In Main.GridMailMerge.Rows.Cast(Of DataGridViewRow)() Where Not row.IsNewRow Select Array.ConvertAll(row.Cells.Cast(Of DataGridViewCell).ToArray, Function(c) If(c.Value IsNot Nothing, c.Value.ToString, ""))
                Using sw As New IO.StreamWriter(appPath & "massmailcsv.txt")
                    sw.WriteLine(String.Join(",", headers))

                    'for each row in the datagridview up to 150, write the file to a csv file and also update the datelastemailed column with today's date
                    For Each r In rows

                        If addedrowcount <= emailcount - 1 Then

                            'make sure they borrower has not been emailed today
                            If Not IsDBNull(Main.GridMailMerge.Item("DateLastEmailedDataGridViewTextBoxColumn", addedrowcount).Value) Then
                                If DateDiff(DateInterval.Day, Main.GridMailMerge.Item("DateLastEmailedDataGridViewTextBoxColumn", addedrowcount).Value, tdate) >= My.Settings.EmailFrequency Then

                                    sw.WriteLine(String.Join(",", r))

                                    'workaround - datagridview will not update the date and commit changes to database if row is selected
                                    If addedrowcount = 0 Then
                                        Main.GridMailMerge.CurrentCell = Main.GridMailMerge.Rows(addedrowcount + 1).Cells(0)
                                    End If

                                    'save the emailed loan numbers to an array
                                    loannumbersemailed(addedrowcount) = Main.GridMailMerge.Item("LoanNumber", addedrowcount).Value

                                    addedrowcount = addedrowcount + 1

                                End If
                            Else
                                sw.WriteLine(String.Join(",", r))

                                'workaround - datagridview will not update the date and commit changes to database if row is selected
                                If addedrowcount = 0 Then
                                    Main.GridMailMerge.CurrentCell = Main.GridMailMerge.Rows(addedrowcount + 1).Cells(0)
                                End If

                                'save the emailed loan numbers to an array
                                loannumbersemailed(addedrowcount) = Main.GridMailMerge.Item("LoanNumber", addedrowcount).Value

                                addedrowcount = addedrowcount + 1

                            End If

                        Else
                            Exit For
                        End If
                    Next

                End Using

            End If

            'PREPARE EMAILS---------------------------------------------------------------

            'make sure there are emails in the datagrid
            If addedrowcount > 1 Then

                'create the email body template and the email body to be sent if not using outlook
                If My.Settings.UseOutlook = "No" And emailaddress <> "" And emailpass <> "" And emailport <> "" And emailhost <> "" And emailssl <> "" Then

                    For g As Integer = 0 To addedrowcount - 1

                        'save the current row to be emailed
                        emailrow = Main.PlannerFilesDataSet.Borrowers.FindByLoanNumber(loannumbersemailed(g))

                        'save the email template to a variable so that it can be altered and filled in with client information
                        emailtemplate = txtBody.Text

                        'Add the data to the email
                        For Each s As String In emailtemplate
                            If s <> "[" And foundtag = 0 Then
                                emailtosend += s
                            Else
                                If s = "[" Then
                                    foundtag = 1
                                ElseIf s <> "[" And s <> "]" And foundtag = 1 Then
                                    tag = tag + s
                                Else
                                    tag = tag.Replace("_", "")
                                    tag = "B" & tag
                                    emailtosend += emailrow.Item(tag)
                                    foundtag = 0
                                    tag = ""
                                End If
                            End If
                        Next

                        'send the email

                        Dim SmtpServer As New SmtpClient()
                        Dim mail As New MailMessage()

                        SmtpServer.Credentials = New Net.NetworkCredential(emailaddress, emailpass)
                        SmtpServer.Timeout = 20000
                        SmtpServer.Port = emailport
                        SmtpServer.Host = emailhost
                        SmtpServer.EnableSsl = emailssl

                        mail = New MailMessage()
                        mail.From = New MailAddress(emailaddress)
                        mail.To.Add(emailrow.Item("BEmail"))
                        mail.Subject = cboTemplates.Text
                        mail.Body = emailtosend
                        SmtpServer.Send(mail)

                        'clear the email template
                        emailtosend = ""

                        'save the successfully emailed borrower email date to the database
                        emailrow.Item("DateLastEmailed") = tdate

                    Next

                    'save updated 'datelastemailed' back to database
                    Main.BorrowersTableAdapter.Update(Main.PlannerFilesDataSet.Borrowers)

                    MsgBox("Your emails have been sent")

                    Me.Dispose()

Open in new window

0
lostinthegame
Asked:
lostinthegame
  • 3
  • 3
1 Solution
 
CodeCruiserCommented:
You can not access any UI controls in DoWork event. You need to move these to ProgressChanged event if you want to update progress.

Following two tutorials demonstrate the use of BGW

http://www.java2s.com/Code/VB/GUI/BackgroundWorkerDemo.htm

http://www.dotnetperls.com/backgroundworker-vbnet
0
 
lostinthegameAuthor Commented:
Now I'm getting somewhere.  The background worker seems to be working by using the Progress Changed event.  My issue is that in the first loop through my DoWork block, the progress changed event doesn't fire.  All subsequent loops fire the event properly.  Any ideas what could be causing this?  My DoWork code is below:

    '   (DO WORK)------------------------------
    Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork

        'resume next on error so that a single failed email will not stop the remaining emails from being sent
        On Error Resume Next

        Dim SmtpServer As New SmtpClient()
        Dim mail As New MailMessage()

        'loop through each available client to be emailed
        For g As Integer = 0 To addedrowcount - 1

            'search the email template for client tags and replace the tag with the client's information
            For Each s As String In emailtemplate
                If s <> "[" And foundtag = 0 Then
                    emailtosend += s
                Else
                    If s = "[" Then
                        foundtag = 1
                    ElseIf s <> "[" And s <> "]" And foundtag = 1 Then
                        etag = etag + s
                    Else
                        etag = etag.Replace("_", "")
                        etag = "B" & etag
                        emailtosend += emailrow.Item(etag)
                        foundtag = 0
                        etag = ""
                    End If
                End If
            Next

            'setup the email login information
            SmtpServer.Credentials = New Net.NetworkCredential(emailaddress, emailpass)
            SmtpServer.Timeout = 20000
            SmtpServer.Port = emailport
            SmtpServer.Host = emailhost
            SmtpServer.EnableSsl = emailssl

            'create the email message
            mail = New MailMessage()
            mail.From = New MailAddress(emailaddress)
            mail.To.Add(emailrow.Item("BEmail"))
            mail.Subject = esubject
            mail.Body = emailtosend
            SmtpServer.Send(mail)

            'clear the email template
            emailtosend = ""

            'save the current client row number so that it can be passed to the progress changed section
            loansaver = g

            'report the successful email to the progress bar percentage changed
            BackgroundWorker1.ReportProgress(((g + 1) / addedrowcount) * 100)

        Next

Open in new window

0
 
CodeCruiserCommented:
Did you step through the code? Does it execute that ReportProgress call? What does the expression

(g + 1) / addedrowcount) * 100

evaluate to on first pass?
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
lostinthegameAuthor Commented:
The first runthrough, using 4 email addresses to test, returns a 25 for (g + 1) / addedrowcount) * 100

g = 0
addedrowcount = 4

I bumped up the number of test emails to 40, and added a breakpoint to check the file numbers being emailed for each loop.  It seems that it's not only the first loop that duplicates.  The output for the file number currently being emailed looks like:

1
1
2
3
4
5
5
6
7
7
8
on and on...

So it seems that for some reason, my ProgressedChanged event isn't changing the variables in my DoWork event every time I call ReportProgress.

My ProgressChanged event is:

    '   (PROGRESS CHANGED)------------------------------
    Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged

            'save the successfully emailed borrower email date to the database
            emailrow.Item("DateLastEmailed") = tdate

            'increase the row by one to move on to the next borrower
            emailrow = Main.PlannerFilesDataSet.Borrowers.FindByLoanNumber(loannumbersemailed(loansaver+1))

            'update the label to show the number of emails sent
            lblemailcount.Text = loansaver

            'increase the progress bar by 1 email
            ProgressBar1.Value = e.ProgressPercentage

 
    End Sub

Open in new window


My loan number should increase by 1 each time this code runs because of the "loansaver+1", but it doesn't.  It only seems to change the loan number sometimes.

From what I have gathered, the issue appears to be that the DoWork event, even though I call ProgressChanged, does not wait for my ProgressChanged event to finish before starting it's next loop.  That means that Dowork runs it's 2nd loop even though the loan number hasn't changed from 1 to 2 yet in my ProgressChanged event.

I think that's the problem, but I don't have any idea how to solve it.

Any ideas?
0
 
CodeCruiserCommented:
Hmm never seen that behaviour. Can you not update that number within DoWork?
0
 
lostinthegameAuthor Commented:
Got it, thanks!

I just had to create variables for my dataset and fill them before the background worker started.  That way I could alter the variable rather than the dataset from within the backroundworker.
0

Featured Post

Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

  • 3
  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now