This is a vb.net mvc 3 application.. I am new to asynchronous and threading combined so this is a bit over my head on 2 levels... I have a long running process that sends mass emails at set intervals to avoid terms of use violations.. Not only for this task but for other options I would like to add a progress bar that is updated through java..I have found a blog post about doing something like this... I have got the following code but there seems to be an issue where the progress bar is never showing...
Below is my extendedTaskRun Class:
Imports System.Collections.Generic
Imports System.Linq
Imports System.Threading
Namespace ExtendedTaskHandler
''' <summary>
''' Long Running Test Class.
''' </summary>
Public Class extendedTaskRun
Private Shared syncRoot As New Object()
''' <summary>
''' Gets or sets the process status.
''' </summary>
''' <value>The process status.</value>
Private Shared Property ProcessStatus() As IDictionary(Of String, Integer)
Get
Return m_ProcessStatus
End Get
Set(value As IDictionary(Of String, Integer))
m_ProcessStatus = value
End Set
End Property
Private Shared m_ProcessStatus As IDictionary(Of String, Integer)
''' <summary>
''' Initializes a new instance of the <see cref="extendedTaskRun"/> class.
''' </summary>
Public Sub New()
If ProcessStatus Is Nothing Then
ProcessStatus = New Dictionary(Of String, Integer)()
End If
End Sub
Public Sub SetStatus(ByVal id As String, ByVal value As Integer)
SyncLock syncRoot
ProcessStatus(id) = value
End SyncLock
End Sub
''' <summary>
''' Processes the long running action.
''' </summary>
''' <param name="id">The id.</param>
Public Function ProcessLongRunningAction(id As String) As String
For i As Integer = 1 To 100
Thread.Sleep(100)
SyncLock syncRoot
ProcessStatus(id) = i
End SyncLock
Next
Return id
End Function
''' <summary>
''' Adds the specified id.
''' </summary>
''' <param name="id">The id.</param>
Public Sub Add(id As String)
SyncLock syncRoot
ProcessStatus.Add(id, 0)
End SyncLock
End Sub
''' <summary>
''' Removes the specified id.
''' </summary>
''' <param name="id">The id.</param>
Public Sub Remove(id As String)
SyncLock syncRoot
ProcessStatus.Remove(id)
End SyncLock
End Sub
''' <summary>
''' Gets the status.
''' </summary>
''' <param name="id">The id.</param>
Public Function GetStatus(id As String) As Integer
SyncLock syncRoot
If ProcessStatus.Keys.Where(Function(x) x = id).Count = 1 Then
Return ProcessStatus(id)
Else
Return 100
End If
End SyncLock
End Function
End Class
End Namespace
Then my controllers are as follows:
Public Function MassEmailStatus() As ActionResult
MassEmailAddressList = TempData("emailaddresses")
TempData.Clear()
TempData.Add("emailaddresses", MassEmailAddressList)
Return View()
End Function
Public Function MassEmailSendingStatus(ByVal id As String) As ActionResult
Dim d As List(Of String) = TempData("emList")
Dim EmailCount As Integer = d.Count
Dim triedCount As Integer = 0
Dim extendedTaskRun As New extendedTaskRun
extendedTaskRun.Add(id)
Dim percentDone As Integer = 0
While Not (triedCount = EmailCount)
For Each em In d
EmailSender(em, String.Empty)
triedCount += 1
percentDone = EmailCount / 100 + triedCount
extendedTaskRun.SetStatus(id, percentDone)
Next
End While
extendedTaskRun.Remove(id)
Return View()
End Function
Then the MassEmailStatus view is as follows:
@Code
ViewData("Title") = "MassEmailSendingStatus"
TempData.Add("emList", TempData("emailaddresses"))
end Code
<div>
<a href="#" id="startProcess">Start Long Running Process</a>
</div>
<br />
<div id="statusBorder">
<div id="statusFill">
</div>
</div>
<script type="text/javascript">
var uniqueId = '@Guid.NewGuid().ToString()';
var tdata = '@TempData("emailaddresses")';
$(document).ready(function (event) {
$('#startProcess').click(function () {
$.post("MassEmailSendingStatus", { id: uniqueId }, function () {
$('#statusBorder').show();
getStatus();
});
event.preventDefault;
});
});
function getStatus() {
var url = 'Admin/GetCurrentProgress/' + uniqueId;
$.get(url, function (data) {
if (data != "100") {
$('#status').html(data);
$('#statusFill').width(data);
window.setTimeout("getStatus()", 100);
}
else {
$('#status').html("Done");
$('#statusBorder').hide();
alert("The Long process has finished");
};
});
}
</script>
These are the additional functions that the blog mentioned and are in my code but from looking at the code I know they cannot be correctly wired up.
Private Delegate Function ProcessTask(id As String) As String
Private extendedRunClass As New extendedTaskRun
''' <summary>
''' Starts the long running process.
''' </summary>
''' <param name="id">The id.</param>
Public Sub StartLongRunningProcess(id As String)
extendedRunClass.Add(id)
Dim processTask As New ProcessTask(AddressOf extendedRunClass.ProcessLongRunningAction)
processTask.BeginInvoke(id, New AsyncCallback(AddressOf EndLongRunningProcess), processTask)
End Sub
''' <summary>
''' Ends the long running process.
''' </summary>
''' <param name="result">The result.</param>
Public Sub EndLongRunningProcess(result As IAsyncResult)
Dim processTask As ProcessTask = DirectCast(result.AsyncState, ProcessTask)
Dim id As String = processTask.EndInvoke(result)
extendedRunClass.Remove(id)
End Sub
''' <summary>
''' Gets the current progress.
''' </summary>
''' <param name="id">The id.</param>
Public Function GetCurrentProgress(id As String) As ContentResult
Me.ControllerContext.HttpContext.Response.AddHeader("cache-control", "no-cache")
Dim currentProgress = extendedRunClass.GetStatus(id).ToString()
Return Content(currentProgress)
End Function
I do know that Im not actually starting the process as im just calling EmailSender(em, String.Empty) which is where the work occurs, inside the for each loop of the MassEmailSendingStatus controller... What must I do to fix this correctly?