locked
Calling a second method while the first method processes in a controller RRS feed

  • Question

  • User994809598 posted

    So I have an issue and hope someone can assist me with it.

    I have a web page that the user clicks a button to upload a file.

    That button passes the file to a controller method (Index) and that controller processes the file and calls other functions then returns at the end a table of what was processed back to the view.

    Everything is all fine and good, but since I'm using an Azure App Service if the processing of the file takes over 240 seconds there is a timeout.

    So I look online and the best solution I could find is to create a partial view with the end table of processed results back to the user every couple of seconds.

    I send the file and do the partial view refreshing via AJAX, but my issue is that after I click the button and send the file to the controller the AJAX continues on, but it seems that the controller ignores the future call to the other method because it is already processing something else. Both methods in the controller are Async Tasks. Any help on what I can do to fix this or if I'm going about it wrong then a push in the right direction is appreciated.

    Here is my AJAX code

    <script>
        $("#btn").click(function () {
            var file = $("#File").get(0);
            var upload = file.files;
            var FileData = new FormData();
            for (var i = 0; i < upload.length; i++) {
                FileData.append(upload[i].name, upload[i])
            }
            $.ajax({
                url: '/BulkCreation/Index',
                type: "POST",
                contentType: false,
                processData: false,
                data: FileData,
                success: function (result) {
                },
                error: function (err) {
                }
            });
            setInterval(function () {
                $.ajax({
                    type: "Get",
                    dataType: "text/html",
                    url: '/BulkCreation/List',
                    success: function (data) {
                        $("#List").html(data);
                    }
                });, 2000);
            }
        });
    </script>

    Friday, March 26, 2021 9:43 AM

All replies

  • User-474980206 posted

    Most likely the controller is using session. actions that use the same session are serialized. disable session or use webapi for the Ajax calls.

    Friday, March 26, 2021 2:48 PM
  • User994809598 posted

    The controller is setting various session variables and since multiple users are accessing and setting these same variables at the same time, I don't want the variables to be overwritten so disabling them isn't something that can be done.

    Friday, March 26, 2021 8:09 PM
  • User-474980206 posted

    then the server can only process one request at a time for the same sessionid. the requests will not run in parallel at the server.

    note: there is a sync lock taken on the session load. this restriction is removed with asp.net core, but then you must manage multiple session updates yourself (default is last one wins).

    in your case as you need session, you might as well combine the two calls into one.

    Friday, March 26, 2021 9:58 PM
  • User-1545767719 posted

    If you are using the Session, can you set the EnableSessionState to the ReadOnly?

    Edit

    Sorry it is my oversight. As your application is MVC the EnableSessionState cannot be used.

    As for the MVC 5.2 application the SessionStateAttribute class is available. Please try if you can apply it to the controller:

    [SessionState(SessionStateBehavior.ReadOnly)]

    SessionStateAttribute Class
    https://docs.microsoft.com/en-us/dotnet/api/system.web.mvc.sessionstateattribute?view=aspnet-mvc-5.2

    Friday, March 26, 2021 10:01 PM
  • User994809598 posted

    Ya this is kind of what I thought. I thought of something that might work, but since Frames aren't really a thing anymore from what I can tell and refreshing the whole page would be undesirable to say the least, I would need to know of a way to refresh a partial view or part of the page without returning from the controller.

    Saturday, March 27, 2021 4:03 AM
  • User1312693872 posted

    Hi,chrisb9907

    There are two ways:

    • Put the setInterval in the success function, it will be triggered when the ajax is successfully executed. And make sure your code format is right, I copied yours but there are some format issue here ( });, 2000);) .
    $("#btn").click(function () {
                $.ajax({
                    url: '/Home/About',
                    type: "POST",
                    success: function (result) {
                        setInterval(function () {
                            $.ajax({
                                type: "Get",
                                url: '/Home/Contact',
                                success: function (data) {
                                    alert("ok");
                                }
                            }), 2000
                        });   
                    },
                    error: function (err) {
                    }
                });             
        });
    • You can try to add [SessionState(SessionStateBehavior.ReadOnly)] on your controller to call multiple ajax at the same time.
    [SessionState(SessionStateBehavior.ReadOnly)]
        public class ***Controller : Controller
    {
    }

    Best Regards,

    Jerry Cai

    Tuesday, March 30, 2021 5:41 AM
  • User994809598 posted

    Hi,chrisb9907

    There are two ways:

    • Put the setInterval in the success function, it will be triggered when the ajax is successfully executed. And make sure your code format is right, I copied yours but there are some format issue here ( });, 2000);) .
    $("#btn").click(function () {
                $.ajax({
                    url: '/Home/About',
                    type: "POST",
                    success: function (result) {
                        setInterval(function () {
                            $.ajax({
                                type: "Get",
                                url: '/Home/Contact',
                                success: function (data) {
                                    alert("ok");
                                }
                            }), 2000
                        });   
                    },
                    error: function (err) {
                    }
                });             
        });
    • You can try to add [SessionState(SessionStateBehavior.ReadOnly)] on your controller to call multiple ajax at the same time.
    [SessionState(SessionStateBehavior.ReadOnly)]
        public class ***Controller : Controller
    {
    }

    Best Regards,

    Jerry Cai

    The first option won't do anything as the full controller will run before sending another request and refreshing the timeout with the Azure service, which is the root at what is trying to be solved. Question about the 2nd option, the second controller doesn't set any session variables so setting the read only session state behavior isn't a problem, but do I need to do that on both controllers being accessed? The second option won't work if both controllers need the session state behavior set as one of the controllers very very much sets some session variables.

    Tuesday, March 30, 2021 8:05 AM
  • User475983607 posted

    Read only means there is no Session lock.  I think you should be able configure the second controller as read only and be able to process a request immediately.   Keep in mind, if the first request sets Session and the second request reads Session, then there is a possibility to read stale data.  We can't see your code.  You'll need to figure that out.

    I solve this type of problem with a background process.  The first request starts the process and returns immediately.  The background process logs its progress in a table.  Now the you can have a timer that reads the table, reports the progress, and eventually loads the data. 

    Tuesday, March 30, 2021 12:16 PM
  • User-1545767719 posted

    Question about the 2nd option, the second controller doesn't set any session variables so setting the read only session state behavior isn't a problem, but do I need to do that on both controllers being accessed? The second option won't work if both controllers need the session state behavior set as one of the controllers very very much sets some session variables.

    Why don't you try to set the SessionStateAttribute to see if your issue can be solved? Although you will not be able to set the attribute to the "one of the controllers" I understand that you can set it to "the second controller".

    Wednesday, March 31, 2021 1:25 AM
  • User994809598 posted

    Read only means there is no Session lock.  I think you should be able configure the second controller as read only and be able to process a request immediately.   Keep in mind, if the first request sets Session and the second request reads Session, then there is a possibility to read stale data.  We can't see your code.  You'll need to figure that out.

    I solve this type of problem with a background process.  The first request starts the process and returns immediately.  The background process logs its progress in a table.  Now the you can have a timer that reads the table, reports the progress, and eventually loads the data. 

    I was thinking of using a background process, but from what I looked up it doesn't get session varables, do you have an example of how you would pass session varaiables to a background worker by chance?

    Why don't you try to set the SessionStateAttribute to see if your issue can be solved? Although you will not be able to set the attribute to the "one of the controllers" I understand that you can set it to "the second controller".

    There is no second controller, all of this is being done in 1 controller. As of now the controller sets and reads session variables

    Wednesday, March 31, 2021 8:45 PM
  • User475983607 posted

    chrisb9907

    I was thinking of using a background process, but from what I looked up it doesn't get session varables, do you have an example of how you would pass session varaiables to a background worker by chance?

    Pass the Session values.  It's the same as calling a method.  

    Wednesday, March 31, 2021 10:05 PM
  • User994809598 posted

    Pass the Session values like calling a method.  

    I guess I'm confused, If I move all the code outside of the controller method into a seperate method and call that method as a background worker I would be using something like the following code:

    public async Task<ActionResult> Index(HttpPostedFileBase postedFile)
            {
               HostingEnvironment.QueueBackgroundWorkItem(
                       ct =>  ProcessFileMethod(postedFile)
                )
               return view();
            }
    

    For session access I'm usually using System.Web.HttpContext.Current.Session. How do I pass the session into the background work item? and once I'm in the ProcessFileMethod how do I write to the Session so that it can be seen outside of that method? This is what I am confused by as the file being processed is an excel file and I am needing to send back a list of said file each time a row from the excel file is processed. Currently that list is being written to a session variable, as multiple users may be doing this at the same time so a regular variable would be overwritten if 2 users were to come in right behind each other.

    Wednesday, March 31, 2021 10:20 PM
  • User-474980206 posted

    Background tasks can not access HttpContext or session. You can pass a copy when you queue the request,  but it can not write back to session. 

    Thursday, April 1, 2021 12:59 AM
  • User994809598 posted

    That's what I read in research, but I didn't know if I just wasn't looking in the right place or was just missing something.

    Thursday, April 1, 2021 1:16 AM
  • User-1545767719 posted

    chrisb9907

    There is no second controller, all of this is being done in 1 controller. As of now the controller sets and reads session variables

    There will be no way other than waiting for completion of the first method to allow the second method to start as long as what you said "the controller sets and reads session variables" is true. It seems that further discussion will not be useful to realize "Calling a second method while the first method processes in a controller" unless you can change the configuration so that the second method does not need to write to the session.

    Thursday, April 1, 2021 1:25 AM
  • User475983607 posted

    This is what I am confused by as the file being processed is an excel file and I am needing to send back a list of said file each time a row from the excel file is processed. Currently that list is being written to a session variable, as multiple users may be doing this at the same time so a regular variable would be overwritten if 2 users were to come in right behind each other.

    To clarify, a static variable is overwritten but not a variable declared within a method or class.  Method scoped variables are unique to the method and cleaned from memory when the method ends. 

    Typically a background task has a unique ID.  The method that starts the background task returns the Id to the client.  The client uses the Id to find the status of the background process.

    The background process uses the key to store information in a table.  Cache is another option for persisting data where the ID is the key for the cached item.

    Thursday, April 1, 2021 11:04 AM