I have this following code. Which is used to call a wcf service async. I cant figure out what the handler does and the args or why it is used(How can i use it). I know that the method returns a task and i use this task to await in the method it is called from. Any help will be appreciated.
public Task<List<Student>> GetStudents()
{
var tcs = new TaskCompletionSource<List<Student>>();
EventHandler<GetStudentsCompletedEventArgs> handler = null;
handler = (sender, args) =>
{
if (args.UserState == tcs)
{
service.GetStudentsCompleted -= handler;
if (args.Error != null)
{
tcs.TrySetException(args.Error);
}
else if (args.Cancelled)
{
tcs.TrySetCanceled();
}
else
{
tcs.TrySetResult(args.Result);
}
}
};
service.GetStudentsCompleted += handler;
service.GetStudentsAsync(tcs);
return tcs.Task;
}
The code converts from the Event Asynchronous Pattern(EAP)(which is exposed by the WCF client) to a task(TAP).
Related
I have an api method which runs in background (async) for ~6 mins.
I want to allow the user to cancel it from another api request.
For Ex.
/api/credits/post/25 will start the background task and returns OK response immediately.
/api/credits/stop/25 will stop running background task.
my current code.
API
[Route("api/[controller]")]
public class CreditController : Controller
{
private readonly CreditService _creditService;
public CreditController (CreditService creditService)
{
_creditService = creditService;
}
[HttpPost("[action]")]
public string Post(int id)
{
if (_creditService.Instances.Any(p => p.ID == id))
return "already running " + id;
_creditService.Post(id, (s,e) =>
{
// sending realtime changes to user using SignalR.
// so that they will see progress and whats running in background
});
return "started " + id;
}
[HttpPost("[action]")]
public string Stop(int id)
{
var instance = _creditService.Instances.SingleOrDefault(p => p.ID == id));
if (instance == null)
return id + " is not running";
instance.CancelToken.Cancel();
return id + " stoppped";
}
}
Service
public class CreditService
{
public static List<CreditService> Instances = new List<CreditService>();
public Status<object> Status = new Status<object>();
public CancellationTokenSource CancelToken = new CancellationTokenSource();
public Task Post(int id, PropertyChangedEventHandler handler = null)
{
Instances.Add(this);
if (handler != null) Status.PropertyChanged += handler;
return Task.Factory.StartNew(() =>
{
try
{
// long running task
}
catch (Exception e)
{
Status.Error = e.Message ?? e.ToString();
}
finally
{
if (handler != null) Status.PropertyChanged -= handler;
Status.Clear();
CancelToken.Dispose();
Instances.Remove(this);
}
});
}
}
This is working as expected. but i want to know is this a good approach or is there any other better alternative to this.
Also this will not work if i use load balancer and multiple instances of application. How do i fix this.
I need to use a 3rd party async web services.
One specific service should return a string. I'm calling it from a xamarin android app and I created the service access logic on a core portable project.
The web service work fine, I tested it on Soap UI and the return is valid (it has two string parameters one the request and a string return value).
This is how I created the service access on core portable library:
public static async Task<string> GetResult(string param2)
{
XSoapClient client = new XSoapClient();
var result = await GetResultAsync(client, PARAM_1, param2);
return result;
}
private static Task<string> GetResultAsync(this XSoapClient #this,
string param1, string param2)
{
var tcs = new TaskCompletionSource<string>();
EventHandler<MyServiceCompletedEventArgs> callback = null;
callback = (sender, args) =>
{
#this.MyServiceCompleted -= callback;
if (args.Cancelled) tcs.TrySetCanceled();
else if (args.Error != null) tcs.TrySetException(args.Error);
else tcs.TrySetResult(args.Result);
};
#this.MyServiceCompleted += callback;
#this.MyServiceAsync(param1, param2);
return tcs.Task;
}
And this how I call this service on a client - xamarin android app in this case:
button.Click += async delegate
{
string param2 = p2EditText.Text;
var result = await ServiceAccessLayer.GetResult(param2);
resultEditText.Text = result;
};
This throws an exception on this part of the web service code:
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
private string EndMyService(System.IAsyncResult result) {
Core.ServiceReference.MyServiceResponse retVal = ((Core.ServiceReference.XSoap)(this)).EndMyService(result);
return retVal.Body.MyServiceResult; // <= this line because Body is null
}
I don't understand why Body is null
EDIT: I also tried this way:
public static void GetResult(string param2)
{
XSoapClient client = new XSoapClient();
client.MyServiceAsync(PARAM_1, param2);
client.MyServiceCompleted += Client_MyServiceCompleted;
}
private static void Client_MyServiceCompleted(object sender, MyServiceCompletedEventArgs e)
{
// do something with e.Result
var result = e.Result;
}
But I got the same error.
Got it
private Task<string> MakeRequest()
{
XSoapClient client = new XSoapClient();
Task<string> request = Task.Factory.FromAsync(
(callback, state) => c.BeginMyService(PARAM_1, param2, callback, state),
result => c.EndMyService(result),
TaskCreationOptions.None);
Task<string> resultTask = request.ContinueWith(response =>
{
return response.Result;
});
return resultTask;
}
public async Task<string> GetResponse()
{
var response = await MakeRequest();
return response;
}
And the call in android app:
button.Click += async delegate
{
string param2 = p2EditText.Text;
var result = await ServiceAccessLayer.GetResponse(param2);
resultEditText.Text = result;
};
Is this the best practice?
I read that sometimes that calling directly a Task can lead to a deadlock of the main thread.
Here's my async method:
public async Task<List<JobsWithSchedules>> fillJobsAsync()
{
IOlapJobAccess jobAccess = new OlapJobAccess(_proxy, CentralPointPath);
List<OlapJob> jobs = await jobAccess.GetAllJobsAsync();
List<JobsWithSchedules> quartzJobs = null;
if (jobs != null)
{
quartzJobs = fillQuartzJobs(jobs);
}
return quartzJobs;
}
I tried a lot of ways to run this task in a sync function. Here's some examples:
public void syncFoo1()
{
var fillJobsTask = fillJobsAsync().ContinueWith((task) =>
{
if (task.Status == TaskStatus.RanToCompletion && task.Result != null)
{
List<JobsWithSchedules> quartzJobs = task.Result;
//...
}
else
{
//...
}
});
fillJobsTask.Wait();
}
public void syncFoo2()
{
Task.Run(() => fillJobsAsync()).ContinueWith((task) =>
{
if (task.Status == TaskStatus.RanToCompletion && task.Result != null)
{
List<JobsWithSchedules> quartzJobs = task.Result;
//...
}
else
{
//...
}
});
}
I want to know which is better solution to run the async method synchronously in the syncFoo() without causing deadlocks. Should I do it like in syncFoo2()?
PS: syncFoo() is called from a a windows service onStart() and onStop().
Since it's in a Windows service, the task should never run synchronously as Stephen suggested. I changed it to be async and it's working fine.
I have this:
public class ScheduledAgent : ScheduledTaskAgent
{
...
protected override void OnInvoke(ScheduledTask task)
{
var userSettings = Utils.LoadSettings();
try
{
var client = new CheckinServiceSoapClient();
var token = WsApiCaller.Token;
var point = ... // User Location;
if (point != null)
{
client.UserTrackAsync(token, userSettings.UserId,
point.Latitude, point.Longitude,
point.Position.Location.HorizontalAccuracy,
point.Position.Location.Altitude,
point.Position.Location.Speed,
point.Position.Location.VerticalAccuracy,
point.Position.Location.Course,
"BACKGROUND_AGENT");
}
}
catch
{
}
NotifyComplete();
}
}
OnInvoke event occurs. But call of UserTrackAsync is not executing.
Your client.UserTrackAsync is an async call. The problem is that NotifyComplete(); is executed before client.UserTrackAsync has a chance to finish.
You need to call it in the UserTrackCompleted handler (and delete it from the original place):
client.UserTrackCompleted += (sender, args) =>
{
var res = args.Result.Retval;
NotifyComplete();
};
Here's the basic setup. We have an ASP.Net WebForms application with a page that has a Flash application that needs to access an external Web Service. Due to (security I presume) limitations in Flash (don't ask me, I'm not a Flash expert at all), we can't connect to the Web Service directly from Flash. The work around is to create a proxy in ASP.Net that the Flash application will call, which will in turn call the WebService and forward the results back to the Flash application.
The WebSite has very high traffic though, and the issue is, if the Web Service hangs at all, then the ASP.Net request threads will start backing up which could lead to serious thread starvation. In order to get around that, I've decided to use an IHttpAsyncHandler which was designed for this exact purpose. In it, I'll use a WebClient to asynchronously call the Web Service and the forward the response back. There are very few samples on the net on how to correctly use the IHttpAsyncHandler, so I just want to make sure I'm not doing it wrong. I'm basing my useage on the example show here: http://msdn.microsoft.com/en-us/library/ms227433.aspx
Here's my code:
internal class AsynchOperation : IAsyncResult
{
private bool _completed;
private Object _state;
private AsyncCallback _callback;
private readonly HttpContext _context;
bool IAsyncResult.IsCompleted { get { return _completed; } }
WaitHandle IAsyncResult.AsyncWaitHandle { get { return null; } }
Object IAsyncResult.AsyncState { get { return _state; } }
bool IAsyncResult.CompletedSynchronously { get { return false; } }
public AsynchOperation(AsyncCallback callback, HttpContext context, Object state)
{
_callback = callback;
_context = context;
_state = state;
_completed = false;
}
public void StartAsyncWork()
{
using (var client = new WebClient())
{
var url = "url_web_service_url";
client.DownloadDataCompleted += (o, e) =>
{
if (!e.Cancelled && e.Error == null)
{
_context.Response.ContentType = "text/xml";
_context.Response.OutputStream.Write(e.Result, 0, e.Result.Length);
}
_completed = true;
_callback(this);
};
client.DownloadDataAsync(new Uri(url));
}
}
}
public class MyAsyncHandler : IHttpAsyncHandler
{
public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
{
var asynch = new AsynchOperation(cb, context, extraData);
asynch.StartAsyncWork();
return asynch;
}
public void EndProcessRequest(IAsyncResult result)
{
}
public bool IsReusable
{
get { return false; }
}
public void ProcessRequest(HttpContext context)
{
}
}
Now this all works, and I THINK it should do the trick, but I'm not 100% sure. Also, creating my own IAsyncResult seems a bit overkill, I'm just wondering if there's a way I can leverage the IAsyncResult returned from Delegate.BeginInvoke, or maybe something else. Any feedback welcome. Thanks!!
Wow, yeah you can make this a lot easier/cleaner if you're on .NET 4.0 by leveraging the Task Parallel Library. Check it:
public class MyAsyncHandler : IHttpAsyncHandler
{
public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
{
// NOTE: the result of this operation is void, but TCS requires some data type so we just use bool
TaskCompletionSource<bool> webClientDownloadCompletionSource = new TaskCompletionSource<bool>();
WebClient webClient = new WebClient())
HttpContext currentHttpContext = HttpContext.Current;
// Setup the download completed event handler
client.DownloadDataCompleted += (o, e) =>
{
if(e.Cancelled)
{
// If it was canceled, signal the TCS is cacnceled
// NOTE: probably don't need this since you have nothing canceling the operation anyway
webClientDownloadCompletionSource.SetCanceled();
}
else if(e.Error != null)
{
// If there was an exception, signal the TCS with the exception
webClientDownloadCompletionSource.SetException(e.Error);
}
else
{
// Success, write the response
currentHttpContext.Response.ContentType = "text/xml";
currentHttpContext.Response.OutputStream.Write(e.Result, 0, e.Result.Length);
// Signal the TCS that were done (we don't actually look at the bool result, but it's needed)
taskCompletionSource.SetResult(true);
}
};
string url = "url_web_service_url";
// Kick off the download immediately
client.DownloadDataAsync(new Uri(url));
// Get the TCS's task so that we can append some continuations
Task webClientDownloadTask = webClientDownloadCompletionSource.Task;
// Always dispose of the client once the work is completed
webClientDownloadTask.ContinueWith(
_ =>
{
client.Dispose();
},
TaskContinuationOptions.ExecuteSynchronously);
// If there was a callback passed in, we need to invoke it after the download work has completed
if(cb != null)
{
webClientDownloadTask.ContinueWith(
webClientDownloadAntecedent =>
{
cb(webClientDownloadAntecedent);
},
TaskContinuationOptions.ExecuteSynchronously);
}
// Return the TCS's Task as the IAsyncResult
return webClientDownloadTask;
}
public void EndProcessRequest(IAsyncResult result)
{
// Unwrap the task and wait on it which will propagate any exceptions that might have occurred
((Task)result).Wait();
}
public bool IsReusable
{
get
{
return true; // why not return true here? you have no state, it's easily reusable!
}
}
public void ProcessRequest(HttpContext context)
{
}
}