javascriptc#.netajaxprogress

Ajax call to get progress using IProgress


I have 2 ajax API calls one fetches data in a long process and and other gets the progress value using IProgress interface and executes every 5 seconds. When hitting the debugger I can see the value updates in ReportProgress and this value gets set to _generateRecipeCardsStatus property. My issue is when calling getProgress() -> GeneratePrintRecipeCardsStatus the property _generateRecipeCardsStatus is always zero where am I going wrong?

    <script>
        getData();
        getProgress()
    
        // long process
        function getData() {
            const token = $('input[name="__RequestVerificationToken"]').val();
        
            $.ajax({
                headers: {
                    'X-Requested-With': 'XMLHttpRequest',
                    "X-ANTI-FORGERY-TOKEN":  token
                },
                type: 'POST',
                url:  `@Url.Action("GeneratePrintRecipeCards", "MenuPrintout")?id=` + @Model.RecipeCardLabelId + `&menuHeaderId=` + @Model.MenuHeaderId, 
                success: function(response){
                     console.log(response)
                     const { errorMsg, progressValue } = response
        
                    inProgressEle.hide();
                    close.show();
        
                    if (errorMsg) {
                        toast(errorMsg, "error")
                    }
                    else if (progressValue > 0) {
                        console.log("progress value:" + progressValue);   
                    }
                    else{ 
                        expand.hide()
                        collapse.hide()
                        statusEle.text("Ready");
                        completedEle.fadeIn() 
                    }
                }
            });
        }
        
         // get progress
        function getProgress() {
             setInterval(function(){ 
                $.ajax({
                    url: '@Url.Action("GeneratePrintRecipeCardsStatus", "MenuPrintout")', 
                    type: "GET",
                    success: function (response) {
                        console.log(response)
                        $('#lblStatus').text(response)
                    }
                })
            }, 5000)
        }
 </script>
 

// controller
public class MenuPrintoutController : BaseController
{
    private readonly IMenuLabelService _menuLabelService;
    private int _generateRecipeCardsStatus;

    private Progress<int> _progress;
    
    public MenuPrintoutController(IMenuLabelService menuLabelServicee)
    {
        _menuLabelService = menuLabelService;
    }
    
    // Is always zero
    public int GeneratePrintRecipeCardsStatus()
    {
        return _generateRecipeCardsStatus;
    }

    // Updates fine 
    private void ReportProgress(int progress)
    {
        _generateRecipeCardsStatus = progress;
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> GeneratePrintRecipeCards(int id, int menuHeaderId)
    {
        try
        {
            _progress = new Progress<int>(ReportProgress);
            
            var data = await _menuLabelService.DoAsync(int menuHeaderId, _progress);
            
            TempData.Put("PrintRecipeCards", data);
            
            return Json(new { Success = true });
        }
        catch (Exception exp)
        {
            return Json(new { ErrorMsg = exp.Message });
        }
    }
}

// service
public interface IMenuLabelService
{ 
    Task<object> DoAsync(int menuHeaderId, IProgress<int> progress);
}

public class MenuLabelService : IMenuLabelService
{
    public async Task<object> DoAsync(int menuHeaderId, IProgress<int> progress) 
    {
        var menuFoodItemsList = _context.FoodItemUnits
            .Where(x => x.MenuSectionFoodItemUnits.Any(y => y.Menusection.MenuheaderId == menuHeaderId))
            .Select(x => x.Fooditem)
            .ToList();
            
        var menuFoodItems = new List<PrintLabel>();
         var donePointer = 0;
         
        foreach (var menuFoodItem in menuFoodItemsList)
        {
            menuFoodItems.Add(menuFoodItem);
            // ...
            
            progress.Report((int)(++donePointer / (double)menuFoodItemsList.Count * 100));
            donePointer++;
        }
        
        return menuFoodItems;
    }
}

Solution

  • A new controller is created for each request, so there are two different _generateRecipeCardsStatus variables.

    If you only want to support a single call on a single server, you can just make the variable static.

    If you want to allow multiple calls, then you'll need some kind of identifier/lookup mechanism. Note that you will also want a way to remove the statuses when they're too old (and presumably no longer needed), so this lookup is less of a dictionary and more of a cache.

    If you want to deploy this to multiple servers, then you'll need an external cache rather than keeping it in memory.