Make WebMethod Async Webform - c#

I have tons of [WebMethod] in whole application and I want all my [WebMethod] to be async. I decorated all my[WebMethod] signatures with async and await keywords and then I run my app. It didn't work out as expected. Let say I have following code
Code Behind
[WebMethod]
public static async Task<List<XXX>> GetXXX()
{
return await new BusinessLogic().GetXXX().ConfigureAwait(false);
}
Business Layer
internal async Task<List<XXX>> GetXXX()
{
var obj = await objDAL.GetXXX().ConfigureAwait(false);
List<XXX> listDO = new List<XXX>();
foreach (var item in obj)
{
listDO.Add(new Countries
{
XXXName = item.name,
XXXID = item.id
});
}
return listDO;
objDAL.GetXXX
internal async Task<List<tblXXX>> GetXXX()
{
using (ABCEntities ctx = new ABCEntities())
{
return await ctx.tblXXX.ToListAsync().ConfigureAwait(false);
}
}
I have put debuggers in the whole path the code above come to return await ctx.tblXXX.ToListAsync(); and then the debugger loses control and the system became non-responsive.
I have found old solutions that doesn't use the power of async
and await.
Question is how I can make my WebMethod Function asyc.
Thanks

Related

Program ends before async result is received

I am writing a program to interact with the Spotify API via a command line.
I have some code here to take a command, and then execute the relevant function to retrieve data from Spotify.
This code shows the problem, I have left out irrelevant code.
public class CommandHandler
{
public async void HandleCommands()
{
var spotifyCommand = GetCommand();
if (spotifyCommand == SpotifyCommand.Current)
{
WriteCurrentSong(await new PlayerController().GetCurrentlyPlayingAsync());
}
if (spotifyCommand == SpotifyCommand.NextTrack)
{
WriteCurrentSong(await new PlayerController().NextTrackAsync());
}
Console.ReadLine();
//end of program
}
}
public class PlayerController
{
public async Task<SpotifyCurrentlyPlaying> NextTrackAsync()
{
using (var httpClient = new HttpClient())
{
//removed code to set headers etc
//Skip Track
var response = await httpClient.PostAsync("https://api.spotify.com/v1/me/player/next", null);
if (response.StatusCode != HttpStatusCode.NoContent)
{
//code to handle this case, not important
}
return await GetCurrentlyPlayingAsync();
}
}
public async Task<SpotifyCurrentlyPlaying> GetCurrentlyPlayingAsync()
{
using (var httpClient = new HttpClient())
{
//removed code to set headers etc
var response = await httpClient.GetAsync("https://api.spotify.com/v1/me/player/currently-playing");
response.EnsureSuccessStatusCode();
return JsonSerializer.Deserialize<SpotifyCurrentlyPlaying>(await response.Content.ReadAsStringAsync());
}
}
}
The two if statements in HandleCommands() call into PlayerController and await the result of the method. For some reason if I use await PlayerController.MethodCall() the call is made, however, the result does not return before the program finishes executing.
Strangely, this is not an issue if I use PlayerController.MethodCall().Result.
Any help will be greatly appreciated, as I would really rather not use .Result. Thanks!
Signature of the HandleCommands is an issue
public async void HandleCommands()
{
// ...
}
You are not showing how this method is called, but I assume it is something like below:
var handler = new CommandHandler();
handler.HandleCommands();
Because of async void method doesn't return Task and caller can not "observe" it's completion.
So application finishes without waiting for task to complete.
To fix - change method signature to below and await for task to complete
public async Task HandleCommands()
{
// ...
}
var handler = new CommandHandler();
await handler.HandleCommands();

Stuck in IMobileServiceTable.ToListAsync()

I have modified the Xamarin/Azure TODO example. But the code is stuck in
IMobileServiceTable.ToListAsync()
This is my IO class:
class DataIO
{
BackgroundWorker DatabaseWorker = new BackgroundWorker();
IMobileServiceTable<UserPosition> PositionTable;
MobileServiceClient client;
public DataIO()
{
Init();
}
public void Init()
{
client = new MobileServiceClient(Constants.ApplicationURL);
PositionTable = client.GetTable<UserPosition>();
}
async void AddEntry(UserPosition entry)
{
await PositionTable.InsertAsync(entry);
}
public async Task<List<UserPosition>> GetEntries()
{
List<UserPosition> Entries = await PositionTable.ToListAsync();
return Entries;
}
public async void DeleteEntry(UserPosition entry)
{
await PositionTable.DeleteAsync(entry);
}
public async void AddToDatabase(UserPosition item)
{
await PositionTable.InsertAsync(item);
}
}
The debugger dosen't neither step over it nor throws an error.
How to handle that?
In an earlier call, there wasn't any problem.
EDIT:
I've rewritten the GetEntries() method to:
public async Task<List<UserPosition>> GetEntries()
{
Task<List<UserPosition>> task = PositionTable.ToListAsync();
List<UserPosition> entries = await task;
return entries;
}
according to this example. But the debugger just stays in the line
Task<List<UserPosition>> task = PositionTable.ToListAsync();
AFAIK, IMobileServiceTable.ToListAsync() would send the request as follows for retrieving the result:
Get https://<your-app-name>.azurewebsites.net/tables/UserPosition
I would recommend you using Fiddler to collect the network traces when calling IMobileServiceTable.ToListAsync(). Also, you could access the table endpoint from your mobile app via the browser to make sure your mobile app could work as expected. Additionally, here is a great tutorial about Handling Data in Mobile Clients, you could refer to it.

How do I return the TaskStatus when making a synchronous call asynchronous?

I have a old data access library that I need to use in my ASP.NET MVC application but I'm having trouble bringing it into the MVC world where many server-side operations are expected to be asynchronous. My data access library looks a like this:
public class MyOldDAL
{
public TaskResult CreateUser(string userName)
{
try
{
// Do user creation
return new TaskResult { Success = true, Message = "success" };
}
catch (Exception ex)
{
return new TaskResult { Success = false, Message = ex.Message };
}
}
}
And is called by my MVC application like this:
public class MyUserStore : IUserStore<ApplicationUser>
{
private readonly MyOldDAL _dal = new MyOldDAL();
public async Task CreateAsync(ApplicationUser user)
{
await Task.Run(() => _dal.CreateUser(user.UserName));
}
}
This is fine until the method in Task.Run() fails for some reason. I'd like to be able to return TaskStatus.Faulted if TaskResult.Success is false but I can't find an example online on how to do it properly - all my Google searches turn up is how to use Task<T> which the IUserStore interface doesn't permit.
For the record, much as I'd like to I can't change MyOldDAL - it's targeting .NET 3.5 so no async or await there!
The normal way to report errors from tasks is via exceptions, so you'll just need to do that transformation yourself:
public class MyUserStore : IUserStore<ApplicationUser>
{
private readonly MyOldDAL _dal = new MyOldDAL();
public Task CreateAsync(ApplicationUser user)
{
var result = _dal.CreateUser(user.UserName);
if (result.Success)
return Task.CompletedTask;
return Task.FromException(new InvalidOperationException(result.Message));
}
}
Note that Task.Run should not be used on ASP.NET.
Note: As Stephen Cleary noticed in his answer, Task.Run should not be used on ASP.NET.
Original answer (before comments):
Your CreateAsync method should normally be like this:
public async Task<TaskResult> CreateAsync(ApplicationUser user)
{
return await Task.Run(() => _dal.CreateUser(user.UserName));
}
But if you can't return Task<TaskResult> from CreateAsync method... well, than you can't obtain TaskResult from CreateAsync by definition. In that case you can store result locally:
private TaskResult taskResult;
public async Task CreateAsync(ApplicationUser user)
{
var result = await Task.Run(() => _dal.CreateUser(user.UserName));
this.taskResult = result;
// process taskResult wherether you need
}
Or raise event with TaskResult payload, allowing client of MyUserStore to subscribe to this event:
public event EventHandler<TaskResult> TaskCompleted;
public async Task CreateAsync(ApplicationUser user)
{
var result = await Task.Run(() => _dal.CreateUser(user.UserName));
this.OnTaskCompleted(result);
}
private void OnTaskCompleted(TaskResult result)
{
this.TaskCompleted?.Invoke(this, result);
}

How to wait for all concurrent Tasks to complete in synchronous code .NET 4.5?

I have the following ServiceStack web service
public class RetreadServices : Service
{
public IRepository Repository { get; set; }
//http://stackoverflow.com/questions/18902135/servicestack-no-server-side-async-support
public async Task<ValidationResponse> Get(RetreadSsnInfoAsync request)
{
return await Task.Factory.StartNew(() =>
{
Tenant.SetTenant(request.TenantId);
return new ValidationResponse { Result = Repository.CannotRetreadSsn(request.Ssn) };
});
}
public async Task<ValidationResponse> Get(RetreadEmailInfoAsync request)
{
return await Task.Factory.StartNew(() =>
{
Tenant.SetTenant(request.TenantId);
return new ValidationResponse { Result = Repository.CannotRetreadEmail(request.Emails) };
});
}
//more methods constructed in the same fashion
}
then in a separate class in my application I have this method (based off of code from #StephenCleary)
private async Task<ValidationResponse[]> DoOperationsConcurrentlyAsync(string tenantId, string[] emails, string ssn)
{
Task<ValidationResponse>[] tasks =
{
ResolveService<RetreadServices>().Get(new RetreadEmailInfoAsync { TenantId = tenantId, Emails = emails }),
ResolveService<RetreadServices>().Get(new RetreadSsnInfoAsync { TenantId = tenantId, Ssn = ssn })
};
await Task.WhenAll(tasks);
ValidationResponse[] result = new ValidationResponse[tasks.Length];
return result;
}
and being invoked like this
synchronous code...
synchronous code...
var result = DoOperationsConcurrentlyAsync(request.TenantId, request.Emails, request.Ssn);
synchronous code...
synchronous code...
The goal of all of this is to wait while the tasks process in parallel and hopefully decrease my over-all time... but the DoOperationsConcurrentlyAsync needs to block, how do I do that? Then as a bonus, how do I pass the return values back from the individual tasks up and out of the method with the async modifier?
I hope I'm understanding this correctly. But don't you just need to add the await keyword?
synchronous code...
synchronous code...
var result = await DoOperationsConcurrentlyAsync(request.TenantId, request.Emails, request.Ssn);
synchronous code...
synchronous code...
EDIT: Bonus question: Again, I hope I'm understanding you correctly...
But I would replace this:
await Task.WhenAll(tasks);
ValidationResponse[] result = new ValidationResponse[tasks.Length];
return result;
...with simply this:
return await Task.WhenAll(tasks);
That should work the way you want it.

When should an action be marked as async?

I have a controller action in mvc 4 app:
public ActionResult Index()
{
GetStartedModel gsModel = new GetStartedModel();
return View(gsModel);
}
and ViewModel:
public class GetStartedModel
{
public IEnumerable<SelectListItem> listA { get; set; }
public IEnumerable<SelectListItem> listB { get; set; }
public GetStartedModel()
{
TestDataWebServiceHelper service = new TestDataWebServiceHelper();
this.GetData(service);
}
private async void SetData(TestDataWebServiceHelper service)
{
listA = await this.SetListA(service);
listB = await this.SetListB(service);
}
private async Task<IEnumerable<SelectListItem>> SetListA(TestDataWebServiceHelper service)
{
List<String> rawList = new List<String>();
rawList = await service.GetValuesAsync("json");
return rawList.Select(x => new SelectListItem { Text = x, Value = x });
}
private async Task<IEnumerable<SelectListItem>> SetListB(TestDataWebServiceHelper service)
{
List<String> rawList = new List<String>();
rawList = await service.GetValuesAsync("json");
return rawList.Select(x => new SelectListItem { Text = x, Value = x });
}
}
When I call this controller action I receive following error:
An asynchronous operation cannot be started at this time. Asynchronous operations may only be started within an asynchronous handler or module or during certain events in the Page lifecycle. If this exception occurred while executing a Page, ensure that the Page is marked <%# Page Async="true" %>.
So, questions:
Should I somehow mark controller or action or page itself as asynchronous to allow this model initialization?
Is it possible to encapsulate all initialization logic to viewmodel and not to pop it to the controller?
What is the reason of that error? Seem like it related to WebForms, but I use Razor engine.
There are two problems in your code:
You shouldn't start async void operations from constructor like this. In fact, you usually shouldn't start any async operations from constructor and you also shouldn't use async void methods at all (except for event handlers).
I think that in your case, async factory method instead of an constructor makes the most sense:
private GetStartedModel()
{}
public static async Task<GetStartedModel> Create()
{
var service = new TestDataWebServiceHelper();
var result = new GetStartedModel();
listA = await result.SetListA(service);
listB = await result.SetListB(service);
return result;
}
For more details, see Stephen Cleary's post on async constructors.
You need to make your controller action async too:
public async Task<ActionResult> Index()
{
var gsModel = await GetStartedModel.Create()
return View(gsModel);
}
There a few things to take care about "async" are as follows :
The method should only be "async" when it needs to wait for the action to be performed by the code written in that.
if there is nothing to be "await" in the method, than that method should not declared as "async".
if there is any event (or else event handler) to be marked as async then return type should be "void" or any specific return type
but if any other (general) method should marked as "async" then the return type should be Task or Task< return-type > .
Now coming to your questions,
definitely you can mark "Controller", "Event" & "Page" as async (I'm
not damn sure about marking the page as async coz i never used it)
and by that those method will take rest until the action will be
performed completely written in that method.
And that's the actual system to encapsulate whole the initialization
logic in a viewModel.
for that make a Floder, named it as "ViewModels" and put all of the viewModel Codes in that folder and whenever needed, use that.
You should put your this code in a ViewModel:
private async void SetData(TestDataWebServiceHelper service)
{
listA = await this.SetListA(service);
listB = await this.SetListB(service);
}
private async Task<IEnumerable<SelectListItem>> SetListA(TestDataWebServiceHelper service)
{
List<String> rawList = new List<String>();
rawList = await service.GetValuesAsync("json");
return rawList.Select(x => new SelectListItem { Text = x, Value = x });
}
private async Task<IEnumerable<SelectListItem>> SetListB(TestDataWebServiceHelper service)
{
List<String> rawList = new List<String>();
rawList = await service.GetValuesAsync("json");
return rawList.Select(x => new SelectListItem { Text = x, Value = x });
}
and then you should call it whenever needed.
(Hope this helps...)

Categories