The Issue:
I'm attempting to call a C# Web Method from jQuery to update a user's profile.
When I implement async and await, the call is made to the Web Method but the call never completes. Chrome forever shows the response as "(Pending)", and the Timing tab shows that the call is "Stalled".
Any input is greatly appreciated.
I've Tried:
Not using async and await
It works! However this entirely defeats the purpose of what I'm trying to achieve.
Changing Task<bool> to void:
"An asynchronous operation cannot be started at this time."
(Yes, my page is marked Async="true")
Googling and searching SO:
I've found a few similar questions but the resulting answers were either "Just make it synchronous instead!" (which entirely defeats the purpose) or they were MVC solutions that I'd rather not employ in my current project.
Code:
$.ajax({
type: "POST",
url: "Profiles.aspx/updateProfileName",
data: JSON.stringify({
profileName: $input.val(),
customer_profile_id: cp_id
}),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: callSuccessful,
error: callFailed
});
[WebMethod]
public static async Task<bool> updateProfileName(string profileName, string customer_profile_id)
{
User user = (User) HttpContext.Current.Session["user"];
if (profileName.Trim().Length == 0) return false;
int customerProfileId = int.Parse(customer_profile_id);
CustomerProfileViewModel profile = new CustomerProfileViewModel();
profile.profile_name = profileName;
profile.customer_profile_id = customerProfileId;
profile.customer_id = user.customerId;
bool profileUpdated = await ExampleApi.UpdateProfile(profile);
return profileUpdated;
}
I'm both sorry and relieved that I'm able to post a solution to my own question such a short time after asking it, but I've come up with a solution for the time being. Although, I'm not going to accept my own answer as I'd still like some input if available.
I've re-factored my Web Method to be a standard public static bool instead of an async. Instead of including the await, it now uses Task.Run() to call the async await function:
public static bool updateProfileWebMethod(string profileName, string customer_profile_id)
{
User user = (User) HttpContext.Current.Session["user"];
if (profileName.Trim().Length == 0) return false;
int customerProfileId = int.Parse(customer_profile_id);
CustomerProfileViewModel profile = new CustomerProfileViewModel();
profile.profile_name = profileName;
profile.customer_profile_id = customerProfileId;
profile.customer_id = user.customerId;
//CALL THE ASYNC METHOD
Task.Run(() => { updateProfileName(profile); });
return true;
}
public static async void updateProfileName(CustomerProfileViewModel profile)
{
bool profileUpdated = await ExampleApi.UpdateProfile(profile);
}
Pretty sad after 2 years nobody has provided an answer. Basically:
bool profileUpdated = await ExampleApi.UpdateProfile(profile).ConfigureAwait(false);
This configures the task so that it can resume on a different thread than it started on. After resuming, your session will no longer be available. If you don't use ConfigureAwait(false), then it waits forever for the calling thread to be available, which it won't be because it is also still waiting further back in the call chain.
However, I run into a problem where the return value doesn't get sent. the asp.net engine that calls the WebMethod is supposed to await the result if Async=true, but that doesn't seem to happen for me.
In this line>
bool profileUpdated = await ExampleApi.UpdateProfile(profile);
This method must also be static and asynchronous, in addition to all others that follow the call stack.
Another important point is to mark your ajax call with async: true,
Related
I've a Web app with a button that makes a call to an API to refresh data:
[AcceptVerbs("GET")]
[Route("refreshFromService/{guid}")]
public HttpResponseMessage RefreshDataFromService(string guid)
{
if (!string.IsNullOrEmpty(guid) && guid.Length == 36 && new Guid(guid) == new Guid("C943F8E4-647D-4044-B19E-4D97FA38EDE0"))
{
new AdData().RefreshCacheAdData(true);
new JdeData().RefreshCacheJdeData(true);
return new HttpResponseMessage(HttpStatusCode.OK);
}
return new HttpResponseMessage(HttpStatusCode.Forbidden);
}
Actually, it's an AJAX call, so in my Network Tab in Google Chrome, I see the request is in pending for 5 minutes.
How can I make this method an async method and how can I refresh my UI to show progress?
EDIT
When I refresh the page, I want the Progress Status to be updated.
First of all, it has nothing to do with a backend. So solution to your problem lies on frontend site. Ajax are asynchronous by nature so you can do something like that.
const loader = document.createElement('span')
// some styles for the loader
// i'm using body just for example purposes
document.body.appendChild(loader)
fetch('refreshFromService/{guid}')
.then(data => {
document.body.removeChild(loader)
// do something with data
return data
})
You have to handle it on UI like this:
function getFlag() {
var option = {
url: '/controllerName/actionName',
data: JSON.stringify({}),
method: 'post',
dataType: 'json',
contentType: 'application/json;charset=utf-8'
};
$.ajax(option).success(function (data) {
$("#picture").append("<img id=\"img1\" src=" + data.img_path + "\ />");
});
};
I am using this code in UI for getting flags at runtime. So you need to write same type of code and get response from the backend.
url: '/controllerName/actionName' is the controller in MVC and then action implemented in that controller.
Request this method in UI with document.ready
I hope I have made sense to you. If still not clear write back i will explain further.
Cheers!
On the user-side, I have a javascript code, that POSTs to the server via JQuery.ajax. On the server-side, I receive the request and handle it, but the user-side script does not wait for the servers response and calls error: getFail (see below) function.
EDIT: problem was due to having the server and the user-side on different domains, in my case different localhosts. See detailed answer by #Jacob below.
How did I check if the user-side waits for the server or not?
I've just run the server in debug mode, and set a breakpoint where it receives the POST-ed values. And the user-side already called the error function.
Could anybody tell me, why it is not working "properly" or what and where is the source of error?
What I suspect to be one of the sources of the "improper" behavior is the dataType parameter of the JQuery.ajax function. It is supposed to specify the expected type of the return value from the server. I am not sure what to set this, so I did not specify hoping the default will find out.
[For the dataType parameter] If none is specified, jQuery will try to infer it based on the MIME type of the response. Possible types are: xml, html, script, json, jsonp, text, multiple. (Source: http://api.jquery.com/jQuery.ajax/, JQuery docs).
Here are the codes:
User-side javascript code:
function logIn() {
try {
alert('try in logIn');
jQuery.ajax({
type: "POST",
url: "http://localhost:8080/Token",
cache: false,
data: {
"grant_type": "password",
"username": document.getElementById("username").value,
"password": document.getElementById("password").value
},
contentType: "application/x-www-form-urlencoded", // data type sent to server
// dataType: "json", // data type expected from server <- THIS may be a source of the problem
success: getSuccess,
error: getFail
});
} catch (e) {
alert(e);
}
function getSuccess(data, textStatus, jqXHR) {
alert(data.Response);
};
function getFail(jqXHR, textStatus, errorThrown) {
//jqXHR.status is 0
//textStatus is "error"
//errorThrown is empty
alert(jqXHR.status); // <-- THIS is what gets called, instantly after the post.
};
};
Server-side C# code:
public class ApplicationOAuthServerProvider
: OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(
OAuthValidateClientAuthenticationContext context)
{
await Task.FromResult(context.Validated()); // break-point was here
}
// This is called by await Task.FromResult(context.Validated())
public override async Task GrantResourceOwnerCredentials(
OAuthGrantResourceOwnerCredentialsContext context)
{
// Here manager will have correct values from the POST-ed data
var manager = context.OwinContext.GetUserManager<ApplicationUserManager>();
var user = await manager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError(
"invalid_grant", "The user name or password is incorrect.");
context.Rejected();
return;
}
foreach (var userClaim in user.Claims)
{
identity.AddClaim(new Claim(userClaim.ClaimType, userClaim.ClaimValue));
}
context.Validated(identity);
}
}
Seeing what the actual error is that you're getting back will confirm/disprove this, but I've seen CORS issues to blame for this. Depending on how the call is being made and what the server supports, you may have a preflight request that is handled by the server as though it was the full request, and then the client can reject the response because it wasn't cross-origin authorized.
I'm not sure what your server-side framework is, but this may help if indeed this is a cross-origin issue:
CORS with WebAPI for XmlHttpRequest
I have an AJAX request when a branch of my JSSTree is clicked
$("#jstree").bind("select_node.jstree", function(evt, data)
{
var idArgument = data.node.text;
$.ajax(
{
type: "POST",
url: "WebForm1.aspx/brancheSelectionnee",
data: JSON.stringify({ id: idArgument }),
contentType: "application/json; charset=utf-8",
success: function(msg)
{
;
}
});
});
So, I call this function, which make a new "page" (because it's static) and call a function that return a System.Web.UI.WebControls.Table.
public static string brancheSelectionnee(string id)
{
var page = (WebForm1)HttpContext.Current.CurrentHandler;
System.Web.UI.WebControls.Table tableau = page.brancheSelectionneeNonStatique(id);
var stringWriter = new StringWriter();
using (var htmlWriter = new HtmlTextWriter(stringWriter))
{
tableau.RenderControl(htmlWriter);
}
string tableauString=stringWriter.ToString();
return "randomstring";
}
Big problem here: My "tableau" is updated, with what I want (I see this with the htmlWriter) but.. I don't know how put it in my screen!
I have it in my C# code, but I want it in the screen, and not just here.
I have "tableauArticle" which is a real System.Web.UI.WebControls.Table, in my ASP.net code.
I tried some things, like putting "tableauArticle" as Static, then
tableauArticles = tableau;
But I didn't see any changement. I think that I updated a table in the page that I don't display
I think that the main problem is that my pagee isn't refresh or I do my tables wrong.
You do an AJAX request, so there is no page refresh. You just get a string (with HTML) back from your server method. You then have to manually put that string on your page. This happens in the success callback function which in your code is empty. As first step try something like this:
success: function(msg)
{
$('<div class="newtable">').html(msg).appendTo('body');
}
On the server-side your method brancheSelectionnee needs the AjaxMethod attribute so that it can be called with AJAX:
[Ajax.AjaxMethod()]
public static string brancheSelectionnee(string id)
(It also should return tableauString; not "randomstring", right?. And I am not sure if you can use the HttpContext.Current.CurrentHandler there, but that is for a second step if the basic AJAX stuff works.)
Here is one tutorial for all this which gives you an overview.
For the answer, it is 100% Raidri solution :
$('#tableauArticles').empty();
$('<div class="newtable">').html(msg.d).appendTo('#tableauArticles');
I want to receive notification from database when table is updated but I need a background thread which call after 1 mint and check database table and show me notification. How can I implement a background thread? I have implemented a thread but when I use while(true) in it, my form is not loaded keep on processing.
Code:
protected void Page_Load(object sender, EventArgs e)
{
t1 = new Thread(new ThreadStart(Function1));
t1.IsBackground = true;
t1.Start();
}
private void Function1()
{
while (true)
{
Thread.Sleep(2000);
count++;
Label1.Text = "Function" + count;
}
}
You have a fundamental misunderstanding of the difference between server-side and client-side code.
For example, your request would require a framework such as SignalR to push a real-time notification to a client.
The easier method is to use Javascript to poll a page of your choice.. for example (jQuery, obviously):
// I'm not a huge jQuery person so theres probably a jQuery way to do this
setInterval(function() {
$.get('/yourPage.aspx', function(response) {
if (response.success) {
alert(response.message);
}
});
}, 5000); // poll a page every 5 seconds.
Then your C# page at /yourPage.aspx can check the database and return a Json object with the properties I've mentioned above.
You need to read up on the difference between Client Side and Server Side.. and how they interact in a stateless protocol such as HTTP.
You can not use background thread on asp.net page. It works on http stateless protocol and the page object is not available after the response it sent. You can only send one response against one request. You can use jQuery ajax , asp.net ajax library timer control or web sockets to fetch data from server periodically.
This post explains how you can fetch data from server using jQuery ajax. This is also very good tutorial to get the data from server using web methods. The example from this tutorial is given below.
Code behind
public partial class _Default : Page
{
[WebMethod]
public static string GetDate()
{
return DateTime.Now.ToString();
}
}
Javascript
$(document).ready(function() {
// Add the page method call as an onclick handler for the div.
$("#Result").click(function() {
$.ajax({
type: "POST",
url: "Default.aspx/GetDate",
data: "{}",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(msg) {
// Replace the div's content with the page method's return.
$("#Result").text(msg.d);
}
});
});
});
You can do async tasks in ASP.NET if you are using the 4.5 framework:
http://www.hanselman.com/blog/TheMagicOfUsingAsynchronousMethodsInASPNET45PlusAnImportantGotcha.aspx
I have a page. In that page some data is shown based on a service call.
This service call may take more than one minute.
So my Index action method have made like that don’t call this long service call.
But this service call is called though an Ajax call on page load.
This model is working
I would like to have a modification to this.
I would like to call this service call in different thread using Task.Factory.StartNew in index action itself. Let that thread be working in background even though the view is returned. And a separate Ajax call I should be able to get the result of the service thread.
The challenge here is how I can access the result of the tread started in Index action method in an Ajax action method?
You could have the Index action (the one that is starting the task) generate an unique number that will be associated to this task (could be a guid) and store an entry into the cache associated to this number. Then return the number to the view.
The task will then be running silently in the background and could update the entry you stored into the cache (with information such as the progression of the task or if you cannot implement this simply indicate whether the task has finished or not). Once the task finishes, remove the entry from the cache.
The view itself could send AJAX requests at regular intervals to another controller action and pass the id of the task. The action will look for the corresponding entry in the cache using this key and return to the view information about the running task. The view itself could then update the UI.
Let's have an example, shall we?
public ActionResult Index()
{
var taskId = Guid.NewGuid().ToString();
var policy = new CacheItemPolicy
{
Priority = CacheItemPriority.NotRemovable,
// Adjust the value to some maximum amount of time that your task might run
AbsoluteExpiration = DateTime.Now.AddHours(1)
};
MemoryCache.Default.Set(taskId, "running", policy);
Task.Factory.StartNew(key =>
{
// simulate a long running task
Thread.Sleep(10000);
// the task has finished executing => we could now remove the entry from the cache.
MemoryCache.Default.Remove((string)key);
}, taskId);
return View((object)taskId);
}
and then you could have another controller action that will be called by the view with an AJAX call to notify the progression of the task:
[HttpPost]
public ActionResult TaskProgress(Guid taskId)
{
var isTaskRunning = MemoryCache.Default.Contains(taskId.ToString());
return Json(new { status = isTaskRunning });
}
and finally you could have the Index view:
#model string
<div id="status">Task with id #Model has been started and running</div>
<script type="text/javascript">
// start continuous polling at 1s intervals
window.setInterval(function() {
$.ajax({
url: '#Url.Action("TaskProgress", new { taskId = Model })',
type: 'GET',
cache: false,
success: function(result) {
if (!result.status) {
// the task has finished executing => let's notify the user
$('#status').html('The task has finished executing');
}
}
});
}, 1000);
</script>
Of course this is just an oversimplified example. In a real world scenario you will have view models, use a complex model for the cache instead of just a simple string where you could hold information about the task and the result of the task if this task need to yield some result after it has finished executing, ...