C# ASP.NET 3.5
Is it possible to "fire and forget" a method in a web service that is not asynchronous or one-way?
There is a method that returns a value (after some processing), which I don't need. It is used by another group (who wrote the service). Basically, it just notifies that user x did action y. I just need to call the method and move on without waiting for the result.
I tried using a BackgroundWorker() with RunWorkerAsync, but the method does not fire for some reason. I cannot change the web service method as I have no access to their code. Should I be using BackgroundWorker, Invoke, ThreadPool, something else? I don't need the result returned, and I don't need to know if it fails. Basically, call the method, and if it works, great. If not, I don't want to stop or slow processing down.
public static void Test()
{
var worker = new BackgroundWorker();
worker.DoWork += Test2;
worker.RunWorkerAsync(new object[] {12345, "test"});
}
private static void Test2(object sender, DoWorkEventArgs e)
{
// Write to log that we got into the method - does not
object[] args = e.Argument as object[];
int num = Convert.ToInt32(args[0]);
string name = (string)args[1];
// call web service method here....
}
Intsead of using Background Worker you can try the following
public static void Test()
{
System.Threading.ThreadPool.QueueUserWorkItem(delegate
{
Test2(new object[] { 12345, "test" });
});
}
private static void Test2(object data)
{
// Write to log that we got into the method - does not
object[] args = data as object[];
int num = Convert.ToInt32(args[0]);
string name = (string)args[1];
// call web service method here....
}
The feature that is requested, by definition IS WCF OneWay. It is impossible to implement the required behavior completely client-side.
IIS by default (a common host of WCF services) is allowed to kill any process that was kicked off by a closed connection. This means that the client must stay connected for the duration of the process (which in effect means waiting for the result). Additionally it is possible the request can timeout and the "fire and forget" process is killed off.
If you only want to reduce the resources taken up at the client to the minimum whilst the request is in flight I would run the request asynchronously. If you only have access to .net<=4.0 then the easiest way to do this is to generate the async calls (I mean APM async and NOT async/await/TPL async) using the "add service reference" option and tick the "generate async methods" option.
You would have to also learn the APM programming model (which is pretty nasty).
Alternatively you could run the WCF call on a separate Thread. But note, this uses significant additional resources.
There is a document here on Asysn/Sync wfc calls. http://msdn.microsoft.com/en-us/library/ms734701.aspx.
Related
I have windows service and WCF web service hosted inside. Infinite task needs to read some logs from device every 2 seconds. In same time web service methods should work properly when they are called. In my case, when i Debug it seems that web service methods calls interrupts Infinite task. So my task is not running on different thread.
How can I optimize my code to work separately from WCF web service? Where is the problem?
On windows service start
protected override void OnStart(string[] args){
//....other code for starting WCF web service....
work();
}
work method:
public async void Work() {
log.Debug("operation started");
Methods checkE = new Methods();
try
{
await checkE.PullLogs();
}
catch (Exception ex) {
log.Error(ex.Message);
}
}
This is PullLogs method:
public async Task PullLogs ()
{
while (true)
{
... some code ...
Parallel.ForEach(tasks, task =>
{
byte[] dataArrayPC;
byte[] dataArrayCT;
byte[] rezult;
PTest p = new PTest();
if (p.PingIt(task.Ip))
{
try
{
SDKCommunication con = new SDKCommunication(task.Id, task.Ip, port, timeout, false);
...some code...
while (indexPC <= indexCT )
{
int broj = con.ReadLogs(address, LOGS, indexPC, 16, 0);
rezult = con.GetLogs(broj);
readEventData(rezult);
indexPC = indexPC + 16;
if (maxiterrations > itteration) {
//send to Java web service
}
itteration++;
}
con.Dispose();
else { log.Debug("error in sdk"); con.Dispose(); }
}
catch (Exception e) { log.Debug(e.Message); }
}
else { log.Error("no connection to device: " + task.Ip); }
}
);
await Task.Delay(2000);
}
}
EDIT:
One more question, is it better to use while(true) and Task.Delay(2000) or have timer tick for every 2 seconds?
Thanks
I'm not sure you are seeing what you think you are seeing. It sounds like you observed with the debugger that WCF service call interrupted your PullLogs code ... but perhaps the code was executing concurrently on different threads, and the debugger just switched you from one to another when a breakpoint was hit or something similar.
In general your WCF service method calls should be executing on the IO Completion Thread Pool. Your TPL code in the Parallel.ForEach should be executing with the default TaskScheduler on the default Thread Pool.
See here for more on specifying your own sychronization context (which will determine which threads WCF code can execute on):
Synchronization Contexts in WCF
If your goal is to make sure that no WCF service calls are processed while your PullLogs code is running, then you will need a synchronization approach, like locking on the same object from the WCF methods, and also from the PullLogs code.
If instead your goal is to make sure that these two parts of your code are isolated, so that both are available to run simultaneously, then I don't think you need to do anything.
Now if you have observed that while your PullLogs code is executing, the WCF service is not as available as you want it to be, then I would take this to indicate that some resource (hardware threads, who knows) is being oversubscribed by the parallel loop in your PullLogs method. In that case probably the best you would be able to do is to naively limit concurrency in that loop to some smaller value ... which might slow down your PullLogs method, but could also go a long way towards making sure your WCF service remains available.
If you want to give this a try, you can create a LimitedConcurrencyTaskScheduler as is done here:
How to: Create a Task Scheduler That Limits Concurrency
and then in your code, supply an instance of this task scheduler to your call to Parallel.ForEach.
Again I don't think this should be necessary unless you have noticed an actual problem (so far you only indicate that you noticed some behavior in the debugger that you didn't expect, but that sounds perfectly reasonable to me).
I have a recent experience I'd like to share that may be helpful to anyone having to maintain a legacy ASMX web service that must be updated to call Task-based methods.
I've recently been updating an ASP.NET 2.0 project that includes a legacy ASMX web service to ASP.NET 4.5. As part of the update, I introduced a Web API interface to allow advanced automation of the application. The ASMX service has to co-exist with the new API for backwards-compatibility.
One of the functions of the application is to be able to request data from external data sources (industrial plant historians, bespoke web services, etc.) on behalf of the caller. As part of the upgrade, I re-wrote substantial parts of the data access layer to asynchronously request data using a Task-based asynchronous pattern. Given that it's not possible to use aync/await in an ASMX service, I modified the ASMX methods to make blocking calls to the asynchronous methods i.e. calling the Task-based method and then using Task.WaitAll to block the thread until the Task completes.
When calling any ASMX method that was calling a method returning Task or Task<T> under the hood, I found that the request always timed out. When I stepped through the code, I could see that that the asynchronous code was successfully executing, but the call to Task.WaitAll never detected that the task had completed.
This caused a major headache: how could the ASMX service happily co-exist with the new asynchronous data access capabilities?
I've recently been updating an ASP.NET 2.0 project that includes a legacy ASMX web service to ASP.NET 4.5.
The first thing to do is ensure that httpRuntime#targetFramework is set to 4.5 in your web.config.
the parent task (i.e. the method call in the ASMX that returned a Task) was never detected as completing.
This is actually a classic deadlock situation. I describe it in full on my blog, but the gist of it is that await will (by default) capture a "context" and use that to resume the async method. In this case, that "context" is an ASP.NET request context, which only allows one thread at a time. So, when the asmx code further up the stack blocks on the task (via WaitAll), it is blocking a thread in that request context, and the async method cannot complete.
Pushing the blocking wait to a background thread would "work", but as you note it is a bit brute-force. A minor improvement would be to just use var result = Task.Run(() => MethodAsync()).Result;, which queues the background work to the thread pool and then blocks the request thread waiting for it to complete. Alternatively, you may have the option of using ConfigureAwait(false) for every await, which overrides the default "context" behavior and allows the async method to continue on a thread pool thread outside the request context.
But a much better improvement would be to use asynchronous calls "all the way". (Side note: I describe this in more detail in an MSDN article on async best practices).
ASMX does allow asynchronous implementations of the APM variety. I recommend that you first make your asmx implementation code as asynchronous as possible (i.e., using await WhenAll rather than WaitAll). You'll end up with a "core" method that you then need to wrap in an APM API.
The wrapper would look something like this:
// Core async method containing all logic.
private Task<string> FooAsync(int arg);
// Original (synchronous) method looked like this:
// [WebMethod]
// public string Foo(int arg);
[WebMethod]
public IAsyncResult BeginFoo(int arg, AsyncCallback callback, object state)
{
var tcs = new TaskCompletionSource<string>(state);
var task = FooAsync(arg);
task.ContinueWith(t =>
{
if (t.IsFaulted)
tcs.TrySetException(t.Exception.InnerExceptions);
else if (t.IsCanceled)
tcs.TrySetCanceled();
else
tcs.TrySetResult(t.Result);
if (callback != null)
callback(tcs.Task);
});
return tcs.Task;
}
[WebMethod]
public string EndFoo(IAsyncResult result)
{
return ((Task<string>)result).GetAwaiter().GetResult();
}
This gets a bit tedious if you have a lot of methods to wrap, so I wrote some ToBegin and ToEnd methods as part of my AsyncEx library. Using these methods (or your own copy of them if you don't want the library dependency), the wrappers simplify nicely:
[WebMethod]
public IAsyncResult BeginFoo(int arg, AsyncCallback callback, object state)
{
return AsyncFactory<string>.ToBegin(FooAsync(arg), callback, state);
}
[WebMethod]
public string EndFoo(IAsyncResult result)
{
return AsyncFactory<string>.ToEnd(result);
}
Upon further investigation, I discovered that sub-tasks created by the initial task could be awaited without any problems, but the parent task (i.e. the method call in the ASMX that returned a Task<T>) was never detected as completing.
The investigation led me to theorise that there was some sort of incompatibility between the legacy Web Services stack and the Task Parallel Library. The solution that I came up with involves creating a new thread to run the Task-based method calls, the idea being that a separate thread would not be subject to thread/task management incompatibilities that existed in the thread processing the ASMX request. To this end I created a simple helper class that will run a Func<T> in a new thread, block the current thread until the new thread terminates and then returns the result of the function call:
public class ThreadRunner<T> {
// The function result
private T result;
//The function to run.
private readonly Func<T> function;
// Sync lock.
private readonly object _lock = new object();
// Creates a new ThreadRunner<T>.
public ThreadRunner(Func<T> function) {
if (function == null) {
throw new ArgumentException("Function cannot be null.", "function");
}
this.function = function;
}
// Runs the ThreadRunner<T>'s function on a new thread and returns the result.
public T Run() {
lock (_lock) {
var thread = new Thread(() => {
result = function();
});
thread.Start();
thread.Join();
return result;
}
}
}
// Example:
//
// Task<string> MyTaskBasedMethod() { ... }
//
// ...
//
// var tr = new ThreadRunner<string>(() => MyTaskBasedMethod().Result);
// return tr.Run();
Running the Task-based method in this way works perfectly and allows the ASMX call to complete successfully, but it's obviously a bit brute-force to spawn a new thread for every asynchronous call; alternatives, improvements or suggestions are welcome!
This may be an old topic but it contains the best answer I have been able to find to help maintain legacy code using ASMX and WebMethod to call newer async functions synchronously.
I am new to contributing to stackoverflow so I don't have the reputation to post a comment to Graham Watts solution. I shouldn't really be responding to another answer - but what other choice do I have.
Graham's answer has proved to be a good solution for me. I have a legacy application that is used internally. Part of it called an external API which has since been replaced. To use the replacement the legacy app was upgraded to .NET 4.7 as the replacement uses Tasks extensively. I know the "right" thing to do would be to rewrite the legacy code but there is no time or budget for such an extensive exercise.
The only enhancement I had to make was to capture exceptions. This may not be the most elegant solution but it works for me.
public class ThreadRunner<T>
{
// Based on the answer by graham-watts to :
// https://stackoverflow.com/questions/24078621/calling-task-based-methods-from-asmx/24082534#24082534
// The function result
private T result;
//The function to run.
private readonly Func<T> function;
// Sync lock.
private readonly object _lock = new object();
// Creates a new ThreadRunner<T>.
public ThreadRunner(Func<T> function)
{
if (function == null)
{
throw new ArgumentException("Function cannot be null.", "function");
}
this.function = function;
}
Exception TheException = null;
// Runs the ThreadRunner<T>'s function on a new thread and returns the result.
public T Run()
{
lock (_lock)
{
var thread = new Thread(() => {
try
{
result = function();
}catch(Exception ex)
{
TheException = ex;
}
});
thread.Start();
thread.Join();
if (TheException != null)
throw TheException;
return result;
}
}
}
// Example:
//
// Task<string> MyTaskBasedMethod() { ... }
//
// ...
//
// var tr = new ThreadRunner<string>(() => MyTaskBasedMethod().Result);
// return tr.Run();
We are calling WCF services asyncronously.
public partial class ServiceClient : System.ServiceModel.ClientBase<MyService>, MyService
{
......
}
ServiceClient _serviceclient;
void Getproducts(string filter, string augument, EventHandler<GetCompletedEventArgs> callback)
{
_serviceclient.GetAsyncGetproducts(filter, argument, callback);
}
I want to the Getproducts function to be synchronous. What is the best way to achieve this like the following
void Getproducts(string filter, string augument, EventHandler<GetCompletedEventArgs> callback)
{
_serviceclient.GetAsyncGetproducts(filter, argument, callback);
//wait until callback comes back and return
}
EDIT: The proxy is providing any synchronous calls
You cannot make synchronous networking requests in Silverlight from the UI thread. There's no going around that. Even if you try to trick the asynchronous methods into behaving synchronously, it will not work. That's because if that were possible, the UI thread would be blocked, and the application would appear to be frozen. This happens because the responses to networking requests in SL are always delivered to the UI thread; if you wait for it on the UI thread itself, then you create a deadlock.
You essentially have two options: the preferred one is to actually go the asynchronous route. It's hard at first, if you're only used to synchronous programming, but it's a very valuable skill to have. The other option is to make the call on a background thread. I've tried it and it works, and some people have blogged about it, so you can try it as well. But AFAIK it's not officially supported.
Rather than just passing the callback parameter as the callback you'll want to assign your own callback that executes that method in addition to doing something else. You effectively just need to trigger an event of some sort. I have demonstrated one way using tasks, but you could just as easily use an auto reset event or one of any number of other synchronization methods.
void Getproducts(string filter, string augument, EventHandler<GetCompletedEventArgs> callback)
{
var tcs = new TaskCompletionSource<bool>();
_serviceclient.GetAsyncGetproducts(filter, argument, args =>
{
callback(args);
tcs.SetResult(true);
});
tcs.Task.Wait();
}
I am new to web services and started out with WCF. I have multiple web service calls, each of which are done asynchronously. But my problem is that the main thread should stop until all the web service calls return and when all the web service calls return only then it should proceed.
I tried two things here :
used a Boolean variable and an infinite loop to stop the main thread. I change the value in the Completed method of the web service call. It resulted in an infinite loop and the Completed never got called.
I made the web service call from the main thread and after making the web service call, I called the Join method on the main thread to stop this thread until the web service returns.
This is the code snippet :
ServerMonitoringBoardDataService.ServerMonitoringBoardDataServiceClient c = new ServerMonitoringBoardDataService.ServerMonitoringBoardDataServiceClient();
c.GetEnvironmentAndServersCompleted += new EventHandler<ServerMonitoringBoardDataService.GetEnvironmentAndServersCompletedEventArgs>(c_GetEnvironmentAndServersCompleted);
c.GetEnvironmentAndServersAsync();
void c_GetEnvironmentAndServersCompleted(object sender, ServerMonitoringBoardDataService.GetEnvironmentAndServersCompletedEventArgs e)
{
var x = e.Result;
}
The reason I am facing problems is that,the multiple web service calls returns data as lists and I have done some operations on this data and then displayed it on the UI.The web service calls are made in a static constructor,so as to fetch the data only once and manipulate and display it many time.
But what happens is that the main thread does not stop until the data is fetched and moves onto perform the operations,where I get a Null Exception.
Please suggest a way out for me and also why the above approaches didn't work.
Thanks in advance for any kind of help on this.
The simplest to code would be to use the async operation model (i.e. you call BeginXXX and it returns an IAsyncResult to you) and follow the procedure outlined here for all of your async calls. This model is supported out of the box if you used the service proxy generator that comes with Visual Studio.
You will start by making the async calls:
var async1 = service.BeginFoo();
var async2 = service.BeginBar();
// more similar calls
And then end them one by one -- order does not matter:
var result1 = service.EndFoo(async1);
var result2 = service.EndBar(async2);
// etc
Of course in practice you will need to add error handling, so it won't be quite as short.
I have two methods on the page. One AddMessageToInboxOutbox(params[]) and other one SendNewMessageMail(params[]).
Once user sends an message to other other first message is added to DB and then sent to recipient's email. Sometimes SMTP is heavy loaded and it takes up to 5 seconds to get answer from it. I want to enclose SendNewMessageMail(params[]) with async call using other thread or something. I have never done that.
How do i perform this action right in ASP.NET?
You can use a delegate to call the method asynchronously:
delegate void SendMailAsync(params object[] args);
static void Main()
{
SendMailAsync del = new SendMailAsync(SendMail);
del.BeginInvoke(new object[] { "emailAddress" }, null, null);
}
static void SendMail(params object[] args)
{
// Send
}
This will use the ThreadPool to assign another thread. In my example, I'm also not registering a callback. Note that this callback, if specified, will be placed onto an unknown ThreadPool thread. Calling EndInvoke on the delegate reference del will block until it completes.
EndInvoke takes the IAsyncResult object that is returned from the BeginInvoke method, if you do this, keep references to the del and IAsyncResult. Also, this isn't specific to ASP.NET.
Note: multi-threading is a powerful mechanism and an in-depth subject to learn. Coupling multi-threading with ASP.NET is arguably more involved than, say, a WinForms application - due to the page lifecycle etc. I'd advise reading up on the multi-threading topic as a whole too.
Alternatively, have a look at ParamaterizedThreadStart