Home > Mobile >  Running a thread in the background when controller return to UI with full results - async call of a
Running a thread in the background when controller return to UI with full results - async call of a

Time:08-14

Hi I have Controller method as below

    [HttpPost]
    public JsonResult Post(string vehiclesString, string Entity, int EntityId, ApplicationUser CurrentUser)
    {
        //https://stackify.com/understanding-asp-net-performance-for-reading-incoming-data/

        List<Vehicle> vehicles = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Vehicle>>(vehiclesString);
        InputFieldController c = new InputFieldController();
        var errors = new List<string>();

        try
        {
            //let's get model for each of the input field
            InputFieldController icController = new InputFieldController();
            List<InputField> fields = icController.GetNamesValues("VehicleField", -1, "Vehicle", 0);

            foreach (Vehicle vehicle in vehicles)
            {
                //convert array of strings into array of input fields
                if (fields.Count != vehicle.ValueStrings.Count)
                {
                    throw new Exception("Vehicle columns mismatch. Expected "
                          fields.Count   " fields, but received "   vehicle.ValueStrings.Count);
                }
                for (int i = 0; i < fields.Count; i  )
                {
                    InputField field = fields[i];
                    string cell = vehicle.ValueStrings[i];

                    if ((cell != null || cell != String.Empty) && (field.Type == "radio" || field.Type == "dropdown"))
                    {
                        var f = field.InputDropdowns.Where(x => x.Name == cell).FirstOrDefault();
                        if (f != null)
                        {
                            field.InputValue.InputDropdownId = f.InputDropdownId;
                        }
                        else
                            field.InputValue.InputDropdownId = null;
                    }
                    else
                    {
                        field.InputValue.Value = cell;
                    }
                    vehicle.Values.Add(field);
                }
                vehicle.Blob = Newtonsoft.Json.JsonConvert.SerializeObject(vehicle.Values);

                Vehicle v = new Vehicle();
                if (vehicle.VehicleId == 0)
                {
                    v = this.DomainLogicUnitOfWork.VehicleManager.Create(vehicle, Entity, EntityId);
                }
            }

            JsonResult data = Json(new
            {
                success = true,
            });

            List<Vehicle> vehiclesList = this.DomainLogicUnitOfWork.VehicleManager.List(Entity, EntityId);
            if (vehiclesList != null)
                foreach (Vehicle v in vehiclesList)
                {
                    if ((v != null) && (v.Blob != null))
                        v.Values = Newtonsoft.Json.JsonConvert.DeserializeObject<List<InputField>>(v.Blob);
                }

            //Task task = Task.Run(async () => await this.DomainLogicUnitOfWork.VehicleInfoManager.CreateOrUpdate(Entity, EntityId));
            /*
             * Here I have to call the this.DomainLogicUnitOfWork.VehicleInfoManager.CreateOrUpdate(string Entity, int EntityId) asynchronously
             * but return the data without waiting for the CreateOrUpdate to complete
             */

            System.Web.Hosting.HostingEnvironment.QueueBackgroundWorkItem(async cancellationToken =>
            {
                await this.DomainLogicUnitOfWork.VehicleInfoManager.CreateOrUpdate(vehiclesList, Entity, EntityId);
            });

            return data;

        }
        catch (Exception ex)
        {
            LogHandler.LogError(9000, "Error updating input fields", ex);
            errors.Add("Error 9000:"   ex.Message);
            return Json(new
            {
                error = ex.Message
            });
        }
    }

And I have CreateOrUpdate method defined as below in VehicleInfoManager class

    public async Task CreateOrUpdate(string Entity, int EntityId)
        {
            //do some stuff
           var task = Task.Run(() => Test(Entity, EntityId));
            //do other stuff
           await task;
            //some more stuff        
}

And Test method is as follows

     private void Test(string Entity, int EntityId)
        {
    List<VehicleInfo> addList; List<VehicleInfo> updateList;
    try
    {
        this.GetAddAndUpdateList(Entity, EntityId, out addList, out updateList);

        if ((addList != null) && (addList.Count > 0))
            using (var cont = this.UnitOfWork.Context)
            {
                foreach (var a in addList)
                {
                    cont.VehicleInfos.Add(a);
                }
                cont.SaveChanges();
            }

        if ((updateList != null) && (updateList.Count > 0))
            using (var cont = this.UnitOfWork.Context)
            {
                foreach (var a in updateList)
                {
                    var aa = cont.VehicleInfos?.Where(x => x.VehicleInfoId == a.VehicleInfoId)?.FirstOrDefault();
                    aa.Address_City = a.Address_City;
                    aa.Address_Country = a.Address_Country;
                    aa.Address_StateCode = a.Address_StateCode;
                    aa.Address_Street1 = a.Address_Street1;
                    aa.Address_Street2 = a.Address_Street2;
                    aa.Address_Zip = a.Address_Zip;
                    aa.ChassisYear = a.ChassisYear;
                    aa.EngineFamilyName = a.EngineFamilyName;
                    aa.Entity = a.Entity;
                    aa.EntityId = a.EntityId;
                    aa.InputFieldEntity = a.InputFieldEntity;
                    aa.InputFieldEntityId = a.InputFieldEntityId;
                    aa.InputFieldGroup = a.InputFieldGroup;
                    aa.LicensePlate = a.LicensePlate;
                    aa.Manufacturer = a.Manufacturer;
                    aa.ModelYear = a.ModelYear;
                    aa.PurchasedDate = a.PurchasedDate;
                    aa.RegHoldClearBy = a.RegHoldClearBy;
                    aa.RegHoldClearDate = a.RegHoldClearDate;
                    aa.RegHoldComment = a.RegHoldComment;
                    aa.RegHoldSet = a.RegHoldSet;
                    aa.RegHoldSetBy = a.RegHoldSetBy;
                    aa.RegHoldSetDate = a.RegHoldSetDate;
                    aa.TrailerPlate = a.TrailerPlate;
                    aa.UpdatedBy = a.UpdatedBy;
                    aa.UpdatedDate = a.UpdatedDate;
                    aa.VehicleId = a.VehicleId;
                    aa.VehicleOperator = a.VehicleOperator;
                    aa.VehicleOwner = a.VehicleOwner;
                    aa.VIN = a.VIN;
                }
                cont.SaveChanges();
            }
    }
    catch (Exception ex)
    {
        ARB.Logging.LogHandler.LogError(9001, "CreateOrUpdate(string Entity, int EntityId) in class VehicleInfoManager", ex);
        throw ex;
    }
}

What I want is, I want two things here

  1. the Post method to call or start the CreateOrUpdate method as background call but instead of waiting until the CreateOrUpdate method finishes, it should return the result data to UI and continue the big task CreateOrUpdate in the background.

  2. Is there anyway to start the background method CreateOrUpdate after sometime like (10 mins etc) post method returns to UI, if it can't be done, its OK we don't have to worry but just asking if there is anyway to trigger this from within the same application

When I implemented it in the above way, even after using System.Web.Hosting.HostingEnvironment.QueueBackgroundWorkItem I am still getting the null http context at the following location

user = System.Web.HttpContext.Current.User.Identity.Name; url = System.Web.HttpContext.Current.Request.Url.ToString();

System.Web.HttpContext.Current is coming out as null. and the application is breaking,

I chaned my async call to the following to use HostingEnvironment.QueueBackgroundWorkItem, still the same htt current context is coming out as null, any help please

System.Web.Hosting.HostingEnvironment.QueueBackgroundWorkItem(async cancellationToken =>
                {
                    await this.DomainLogicUnitOfWork.VehicleInfoManager.CreateOrUpdate(Entity, EntityId);
                });

Thank you

CodePudding user response:

System.Web.HttpContext.Current is maintained by the ASP.NET's SynchronizationContext.

When you start a new Task, the code will be executing on another thread pool thread without a SynchronizationContext and the value of System.Web.HttpContext.Current is not safe to use, whatever its value.

When the execution of the action method (Post) ends, the request will end and the HttpContext instance will be invalid, even if you mange to get a reference to it.

Also, there is no guarantee that that code you posted to the thread pool will run to complete, since it's out of ASP.NET control and ASP.NET won't be aware of it if IIS decides, for some reason, to recycle the application pool or the web application.

If you to post background work, use HostingEnvironment.QueueBackgroundWorkItem. Beware if its constraints.

  • Related