Results 1 to 4 of 4

Thread: [RESOLVED] Sending Multiple Emails Drawback

  1. #1

    Thread Starter
    Lively Member FunkySloth's Avatar
    Join Date
    Aug 2016
    Posts
    91

    Resolved [RESOLVED] Sending Multiple Emails Drawback

    Hi,

    I have this ActionResult called SendEmails, what it does are as follows:

    1. Read data in my Entity Model.
    2. Send Email with attachment to the result data of #1.
    3. Read data in my Entity Model and stored in a List<Model>
    4. Insert data in database by looping through the of List<Model> and add the result data of #3 in New Mail Address.
    5. Send Email to all result data in #4.

    This is using Synchronous Action and working fine, however, the drawback is that on the screen of the user the pre-loader takes times (due to it takes time to finish all the process).

    Is there a way to finish all the process in the background, so that the user can navigate the application without waiting to pre-loader to end. Just like Google Gmail, after you send the email, you can navigate and do something else, while it will give you a notification on the top of the screen, telling if the email successfully send or not.

    I read about Asynchronous Action but I think it's not the thing that will do base on what I want to do.

    Help Appreciated.

    Thank you

  2. #2
    Smooth Moperator techgnome's Avatar
    Join Date
    May 2002
    Posts
    34,532

    Re: Sending Multiple Emails Drawback

    Well there is only Synch or Asynch... there are no other options. Synchronous calls wait to be completed before continuing... Asynchronous means you call it and it returns right away even as it does it's own thing... which sounds like exactly what you DO need.

    -tg
    * I don't respond to private (PM) requests for help. It's not conducive to the general learning of others.*
    * I also don't respond to friend requests. Save a few bits and don't bother. I'll just end up rejecting anyways.*
    * How to get EFFECTIVE help: The Hitchhiker's Guide to Getting Help at VBF - Removing eels from your hovercraft *
    * How to Use Parameters * Create Disconnected ADO Recordset Clones * Set your VB6 ActiveX Compatibility * Get rid of those pesky VB Line Numbers * I swear I saved my data, where'd it run off to??? *

  3. #3
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,302

    Re: Sending Multiple Emails Drawback

    Performing operations that are asynchronous from the perspective of the user in web apps can be a pain. There's obviously more to it than just using multiple threads. We've recently deployed an ASP.NET Core (.NET Framework) application in my office where we use Exchange to send meeting invitations and that can take some time. The user clicks a button to initiate the operation and then they see a progress bar fill as invitations sent. The code is C# but the principle is the same in VB. Here's some of the relevant code, i.e. script and controller actions:
    javascript Code:
    1. function startSendInvitations()
    2. {
    3.     if ($("#sendInvitationsButtonDiv") != null)
    4.     {
    5.         SI.Dpc.EventsHub.disableButtons(["#saveButtonDiv", "#sendInvitationsButtonDiv"]);
    6.  
    7.         $("#sendInvitationsButton").html("Sending Invitations...");
    8.  
    9.         SI.DataTransfer.jsonPostToJson("@Url.Content($"~/meetings/sendmeetinginvitationsstart/{Model.MeetingId}")",
    10.             @Model.MeetingId,
    11.             runSendInvitations);
    12.     }
    13. }
    csharp Code:
    1. [HttpPost]
    2. public async Task<IActionResult> SendMeetingInvitationsStart(long id)
    3. {
    4.     //this action (plus SendMeetingInvitationsRun and SendMeetingInvitationsProgress) need to cater for:
    5.     //If user calls this action more than once very quickly eg via a double click
    6.     //If two or more users call this action at the same time for the same meeting
    7.  
    8.     var currentUserId = GetCurrentUserId();
    9.     var meetingDto = meetingService.GetMeeting(id);
    10.     var meetingInvitationSectionAccessLevels = await GetCurrentUserMeetingTypeSectionAccessLevelAsync(meetingDto.MeetingTypeId,
    11.                                                                                                       MeetingTypeSection.Invitations,
    12.                                                                                                       true);
    13.  
    14.     var longRunningProcessReturnModel = new LongRunningProcessReturnModel();
    15.  
    16.     if (meetingInvitationSectionAccessLevels >= AccessLevel.Edit)
    17.     {
    18.         LongRunningProcessDto longRunningProcessDto = null;
    19.  
    20.         //check if invitations are currently being sent
    21.         var longRunningProcessDtos = await longRunningProcessService.GetLongRunningProcessByTypeAndOwnerIdAsync(LONG_RUNNING_PROCESS_TYPE_MEETING_SEND_INVITATIONS, id);
    22.  
    23.         if (longRunningProcessDtos.Count == 1)
    24.         {
    25.             //use first existing long running process
    26.             longRunningProcessDto = longRunningProcessDtos[0];
    27.         }
    28.         else if (longRunningProcessDtos.Count > 1)
    29.         {
    30.             //use first existing long running process
    31.             longRunningProcessDto = FindFirstLongRunningProcessAndDeleteOthers(longRunningProcessDtos);
    32.         }
    33.  
    34.         //check if we need to create a new long running process
    35.         if (longRunningProcessDto == null)
    36.         {
    37.             //add new long running process record to database
    38.             longRunningProcessDto = new LongRunningProcessDto();
    39.             longRunningProcessDto.Progress = 0;
    40.             longRunningProcessDto.OwnerId = id;
    41.             longRunningProcessDto.Type = LONG_RUNNING_PROCESS_TYPE_MEETING_SEND_INVITATIONS;
    42.             await longRunningProcessService.SaveLongRunningProcessAsync(longRunningProcessDto);
    43.         }
    44.  
    45.         longRunningProcessReturnModel = Mapper.Map<LongRunningProcessDto, LongRunningProcessReturnModel>(longRunningProcessDto);
    46.         longRunningProcessReturnModel.IsSuccessful = true;
    47.     }
    48.  
    49.     LogLongRunningProcessReturnModel(longRunningProcessReturnModel, "Meetings", "SendMeetingInvitationsStart");
    50.  
    51.     return Json(longRunningProcessReturnModel);
    52. }
    javascript Code:
    1. function runSendInvitations(data, textStatus, jqXHR)
    2. {
    3.     if ($("#sendInvitationsButtonDiv") != null)
    4.     {
    5.         if (data.isSuccessful)
    6.         {
    7.             //update progress
    8.             updateSendInvitationsProgress(data.progress);
    9.  
    10.             SI.DataTransfer.jsonPostToJson("@Url.Content($"~/meetings/sendmeetinginvitationsrun/{Model.MeetingId}")",
    11.                 @Model.MeetingId,
    12.                 displaySendInvitationsProgress);
    13.         }
    14.         else
    15.         {
    16.             afterSendInvitations(data, textStatus, jqXHR);
    17.         }
    18.     }
    19. }
    csharp Code:
    1. [HttpPost]
    2. public async Task<IActionResult> SendMeetingInvitationsRun([FromBody] long id)
    3. {
    4.     var longRunningProcessReturnModel = new LongRunningProcessReturnModel();
    5.  
    6.     var currentUserId = GetCurrentUserId();
    7.     var meetingDto = meetingService.GetMeeting(id);
    8.     var meetingInvitationSectionAccessLevels = await GetCurrentUserMeetingTypeSectionAccessLevelAsync(meetingDto.MeetingTypeId,
    9.                                                                                                       MeetingTypeSection.Invitations,
    10.                                                                                                       true);
    11.  
    12.     if (meetingInvitationSectionAccessLevels >= AccessLevel.Edit)
    13.     {
    14.         LongRunningProcessDto longRunningProcessDto = null;
    15.  
    16.         //check if invitations are currently being sent
    17.         var longRunningProcessDtos = await longRunningProcessService.GetLongRunningProcessByTypeAndOwnerIdAsync(LONG_RUNNING_PROCESS_TYPE_MEETING_SEND_INVITATIONS, id);
    18.  
    19.         if (longRunningProcessDtos.Count == 1)
    20.         {
    21.             //use first existing long running process
    22.             longRunningProcessDto = longRunningProcessDtos[0];
    23.         }
    24.         else if (longRunningProcessDtos.Count > 1)
    25.         {
    26.             //use first existing long running process
    27.             longRunningProcessDto = FindFirstLongRunningProcessAndDeleteOthers(longRunningProcessDtos);
    28.         }
    29.  
    30.         //check if process has started
    31.         if (longRunningProcessDto != null && !longRunningProcessDto.StartedAt.HasValue)
    32.         {
    33.             longRunningProcessDto.StartedAt = DateTime.Now;
    34.             await longRunningProcessService.SaveLongRunningProcessAsync(longRunningProcessDto);
    35.  
    36.             longRunningProcessReturnModel = Mapper.Map<LongRunningProcessDto, LongRunningProcessReturnModel>(longRunningProcessDto);
    37.  
    38.             var baseUrl = Url.GetBaseUrl();
    39.  
    40.             //we do not want to wait for this task to complete before returning
    41.             var task = Task.Run(async () =>
    42.                     {
    43.                         try
    44.                         {
    45.                             //set up progress callback
    46.                             var progress = new Progress<int>(async percent =>
    47.                             {
    48.                                 //record progrees in long running process
    49.                                 longRunningProcessDto.Progress = percent;
    50.                                 await longRunningProcessService.SaveLongRunningProcessAsync(longRunningProcessDto);
    51.                             });
    52.  
    53.                             await invitationService.AddAndSendMeetingInvitationsAsync(id, currentUserId, baseUrl, progress);
    54.  
    55.                             //to be neat save with 100% first
    56.                             longRunningProcessDto.Progress = 100;
    57.                             await longRunningProcessService.SaveLongRunningProcessAsync(longRunningProcessDto);
    58.  
    59.                             //delete long running process
    60.                             await longRunningProcessService.DeleteLongRunningProcessAsync(longRunningProcessDto.LongRunningProcessId);
    61.                         }
    62.                         catch (Exception ex)
    63.                         {
    64.                             logger.Log(NLog.LogLevel.Error, ex);
    65.                         }
    66.                     }).ConfigureAwait(false);
    67.  
    68.             longRunningProcessReturnModel.IsSuccessful = true;
    69.         }
    70.     }
    71.  
    72.     LogLongRunningProcessReturnModel(longRunningProcessReturnModel, "Meetings", "SendMeetingInvitationsRun");
    73.  
    74.     return Json(longRunningProcessReturnModel);
    75. }
    javascript Code:
    1. function displaySendInvitationsProgress(data, textStatus, jqXHR)
    2. {
    3.     //update progress
    4.     updateSendInvitationsProgress(data.progress);
    5.  
    6.     if (!data.hasProcessCompleted)
    7.     {
    8.         //get progress again
    9.         setTimeout(function()
    10.                 {
    11.                     SI.DataTransfer.jsonPostToJson('@Url.Content("~/meetings/sendmeetinginvitationsprogress")',
    12.                         @Model.MeetingId,
    13.                         displaySendInvitationsProgress);
    14.                 },
    15.             2000);
    16.     }
    17.     else
    18.     {
    19.         afterSendInvitations(data, textStatus, jqXHR);
    20.     }
    21. }
    22.  
    23. function updateSendInvitationsProgress(progress)
    24. {
    25.     if (progress != null)
    26.     {
    27.         if ($("#sendInvitationsButtonDiv").hasClass('ButtonDisabled'))
    28.         {
    29.             //update progress text
    30.             $("#sendInvitationsButton").html("Sending Invitations..." + progress + "%");
    31.         }
    32.     }
    33. }
    csharp Code:
    1. [HttpPost]
    2. public async Task<IActionResult> SendMeetingInvitationsProgress([FromBody] long id)
    3. {
    4.     LongRunningProcessReturnModel longRunningProcessReturnModel = null;
    5.  
    6.     //get any long running processes
    7.     var longRunningProcessDtos = await longRunningProcessService.GetLongRunningProcessByTypeAndOwnerIdAsync(LONG_RUNNING_PROCESS_TYPE_MEETING_SEND_INVITATIONS, id);
    8.  
    9.     //check if any have been running for too long
    10.     foreach (var longRunningProcessDtoToCheck in longRunningProcessDtos)
    11.     {
    12.         if (longRunningProcessDtoToCheck.StartedAt.HasValue &&
    13.             longRunningProcessDtoToCheck.StartedAt.Value.AddMinutes(LONG_RUNNING_PROCESS_TYPE_MEETING_SEND_INVITATION_MAX_RUN_TIME_IN_MINUTES) < DateTime.Now)
    14.         {
    15.             //delete long running process
    16.             await longRunningProcessService.DeleteLongRunningProcessAsync(longRunningProcessDtoToCheck.LongRunningProcessId);
    17.  
    18.             //clear start date for use below
    19.             longRunningProcessDtoToCheck.StartedAt = null;
    20.         }
    21.     }
    22.  
    23.     LongRunningProcessDto longRunningProcessDto = null;
    24.     if (longRunningProcessDtos.Count == 1)
    25.     {
    26.         //use first existing long running process
    27.         longRunningProcessDto = longRunningProcessDtos[0];
    28.     }
    29.     else if (longRunningProcessDtos.Count > 1)
    30.     {
    31.         //use first existing long running process
    32.         longRunningProcessDto = FindFirstLongRunningProcessAndDeleteOthers(longRunningProcessDtos);
    33.     }
    34.  
    35.     if (longRunningProcessDto != null)
    36.     {
    37.         longRunningProcessReturnModel = Mapper.Map<LongRunningProcessDto, LongRunningProcessReturnModel>(longRunningProcessDto);
    38.     }
    39.     else
    40.     {
    41.         //assume long running service has already been completed and deleted
    42.         longRunningProcessReturnModel = new Models.LongRunningProcessReturnModel();
    43.         longRunningProcessReturnModel.HasProcessCompleted = true;
    44.  
    45.         //check if was successful
    46.         var meetingDto = meetingService.GetMeeting(id);
    47.         if (!string.IsNullOrWhiteSpace(meetingDto?.ExchangeAppointmentId))
    48.         {
    49.             longRunningProcessReturnModel.WasProcessSuccessful = true;
    50.         }
    51.     }
    52.  
    53.     LogLongRunningProcessReturnModel(longRunningProcessReturnModel, "Meetings", "SendMeetingInvitationsProgress");
    54.  
    55.     return Json(longRunningProcessReturnModel);
    56. }
    javascript Code:
    1. function afterSendInvitations(data, textStatus, jqXHR)
    2. {
    3.     if (data.wasProcessSuccessful)
    4.     {
    5.         SI.UI.showResult("sendInvitationsButton",
    6.             "Invitations Sent",
    7.             "Successful",
    8.             "sendInvitationsButtonDiv",
    9.             function ()
    10.             {
    11.                 if ($("#sendInvitationsButtonDiv") != null)
    12.                 {
    13.                     $("#sendInvitationsButton").html("Send Invitations");
    14.                     $("#sendInvitationsButtonDiv").hide();
    15.                 }
    16.  
    17.                 $("#syncInvitationResponses").removeClass('Disabled');
    18.                 $("#resendNoResponseInvitations").removeClass('Disabled');
    19.                 $("#deleteInvitations").removeClass('Disabled');
    20.             });
    21.     }
    22.     else
    23.     {
    24.         SI.UI.showResult("sendInvitationsButton",
    25.             "Send Invitations Failed",
    26.             "Failed",
    27.             "sendInvitationsButtonDiv",
    28.             function ()
    29.             {
    30.                 $("#sendInvitationsButton").html("Send Invitations");
    31.             });
    32.     }
    33.  
    34.     SI.Dpc.EventsHub.enableButtons(["#saveButtonDiv", "#sendInvitationsButtonDiv"]);
    35.  
    36.     reloadInvitationsIfLoaded();
    37. }

  4. #4

    Thread Starter
    Lively Member FunkySloth's Avatar
    Join Date
    Aug 2016
    Posts
    91

    Re: Sending Multiple Emails Drawback

    @jm - that suffice the work that I need.

    Thank you for the replies.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width