WCF using Async/Await - Channel blocked in Client - wcf

I'm working with .Net 4.5 and my services are written using Async and returning Task<>.
When I call the service from the client I use await.
When the service returns after the await it seems that the channel is blocked if the client code calls another operation which is not async.
Sample code:
value = await Service.InitAsync();
Service.SyncOperation(); // Further callbacks/return values from Server are blocked and this statement never returns
(The whole point of async/await as I see it is to allow me to write the full flow in one method and let the framework deal with threads and callbacks)
I was able to solve this by wrapping the call to the service with a new task and then Task.Yield:
async Task<T> CallAsync<T>(Func<Task<T>> func)
{
Task<T> t = await func;
await Task.Yield();
return t;
}
and in the client:
value = CallAsync(Service.InitAsync);
Service.SyncOperation(); //Now the server is not blocked since Yield changed back to the client context.
My question is, is there any other way to do this without wrapping the Service call in the Client?
Maybe some attribute or property in the Service?
Thanks

Related

WCF: Method return a response, after spawning a thread to do background processing

So I have the following scenario. I have a method in my WCF, where the client will send a request, the WCF service would then perform some background processing and do call an external webservice method, and the method will respond with an acknowledgement immediately (before the background processing has been completed).
The way I have thought of doing is having my WCF method return a response after spawning a thread to do the background processing, and call the external webservice. The flow is something like this:
Caller sends request to INITIAL_CALL
WCF starts a thread that calls PROCESS
WCF returns true
PROCESS makes call to EXTERNALWS and gets response in postResponse
postReponse gets logged to the database
See example code below:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class Service : IService
public bool INITIAL_CALL()
{
new Thread(()=>
{
PROCESS();
}).Start();
return true;
}
private void PROCESS()
{
//Do some background processing and create request for call below
var processRequest = "Request goes here";
using (var client = new EXTERNALWS.ResponseTypeClient())
{
var postResponse = client.POST(processRequest);
//Log postResponse to database
}
}
Having in mind that PROCESS() may run for a long time, I just wanted to see if there is a better way of doing this with WCF and IIS? Or if there are any pitfalls that I have to consider i.e IIS app pool recycling destroying the thread.
I have found a solution for this. I ended up using Hangfire to do the background processing needed (https://www.hangfire.io/). Hangfire seems to be specifically made for this. I have implemented it following the documentation found at their homepage, in a separate ASP MVC application. I have also configured it as always running on IIS. All instructions and sample codes to setup Hangfire to do this are found here https://docs.hangfire.io/en/latest/index.html. I had to change the flow (since I am not spawning any new threads manually as previously), and also create a new table in the database so that the INITIAL_CALL in the WCF Application would queue all the long running jobs (later to be picked up and executed by Hangfire). Have in mind this is seperate from Hangfire's queue, this table will be checked by Hangfire in a predefined interval, and will check this database table that stores which function to call, its parameters, and an indicator if the job has already been picked up by Hangfire or not (to avoid the re-entrant scenario described here https://docs.hangfire.io/en/latest/best-practices.html). A little extra work, but works like a charm.
The way the flow works now is as follows:
Caller sends request to INITIAL_CALL
In INITIAL_CALL, an entry is made in a new database table (this is the job
queue that will be checked by Hangfire in a predefined interval).
INITIAL_CALL returns true
Hangfire checks this database table in a predefined interval using PROCESS_JOBS (this interval can be defined in Hangfire itself).
If there is a queued item, PROCESS_JOBS proceeds and makes the call to EXTERNALWS and gets response in postResponse. If not, it just exits and does nothing further.
postReponse gets logged to the database.
See updated example code below:
WCF Application
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class Service : IService
public bool INITIAL_CALL()
{
//Add job queue entry in database table to be picked up by Hangfire
return true;
}
Hangfire Application
public void PROCESS_JOBS()
{
//Check in a predefined interval if there is a pending job in the queue.
//If there is continue with below, otherwise exit function.
//Do some background processing and create request for call below
var processRequest = "Request goes here";
using (var client = new EXTERNALWS.ResponseTypeClient())
{
var postResponse = client.POST(processRequest);
//Log postResponse to database
}
}

RabbitMQ and Webjob SDK. Intentionally failing function

I have a webjob that is listening to a rabbitMQ queue and will pick messages of the queue and post to another service.
However, if the service fails (returns 5xx errors or similar) I would like to put the message back into the queue and try again later.
I am using a rabbitmq extension to webjob sdk (https://github.com/Sarmaad/WebJobs.Extensions.RabbitMQ/tree/master/WebJobs.Extensions.RabbitMQ) and if I understand correctly this will happend if the webjob "function" fails. Is there a way of failing it intentionally? (Other than throwing exceptions)
From the extension source code:
var result = _executor.TryExecuteAsync(new TriggeredFunctionData{TriggerValue = triggerValue}, CancellationToken.None).Result;
if (result.Succeeded)
_channel.BasicAck(args.DeliveryTag, false);
else
_channel.BasicNack(args.DeliveryTag, false, false);
According to Azure WebJob SDK, we know that the WebJobs status depends on whether your WebJob/Function is executed without any exceptions or not. We can't set the finial status of a running WebJob programmatically.
Code from TriggeredFunctionExecutor class.
public async Task<FunctionResult> TryExecuteAsync(TriggeredFunctionData input, CancellationToken cancellationToken)
{
IFunctionInstance instance = _instanceFactory.Create((TTriggerValue)input.TriggerValue, input.ParentId);
IDelayedException exception = await _executor.TryExecuteAsync(instance, cancellationToken);
FunctionResult result = exception != null ?
new FunctionResult(exception.Exception)
: new FunctionResult(true);
return result;
}
Is there a way of failing it intentionally? (Other than throwing exceptions)
So the answer to your question is no. Throwing exceptions is the only way to do it.

Is ChannelFactory with async methods in .NET 4.5 supported?

I'm in the process of converting our WCF services to use async programming. As expected the interface looks like this:
public interface IFoo
{
Task<string> DoSomething(string request);
}
On the client side I'm not creating a service reference from Visual Studio. Since I own both client and server I just share the interface with the client, then I create (and cache) a ChannelFactory. To invoke the method asynchronously I use the expected syntax:
IFoo clientChannel = channelFactory.CreateChannel();
bool result = await clientChannel.DoSomething("Hello World");
My question is: Is the IClientChannel generated by the ChannelFactory really using the async features underneath? I mean, can I be sure there's no thread blocked waiting for the server response?
Looking at the ClientBase code, async calls all boil down to:
http://referencesource.microsoft.com/#System.ServiceModel/System/ServiceModel/Channels/ServiceChannel.cs,0353de22100bb396
There doesn't seem to be any reason to think the thread would block waiting for a network response.

Openning Async WCF Service with wrong Username-Password hangs

I have a WCF service that implements the 'Custom-Username-Password-Validator'.
The service itself checks the username+password against a local file,
and if there is no match - it throws a FaultException with a message.
.
When I use the service synchronously it works fine.
When I go to work with it ASYNC, I have a problem.
If I pass the wrong 'Username+Password' credentials - and open the client,
instead of returning immediatly from the service going into my 'Channel_Faulted()' method,
the client thread simply waits until the Timeout triggers,
and then I get a 'TimeoutException'.
try
{
client = new MyServiceClient("WSDualHttpBinding_IMyervice");
client.ClientCredentials.UserName.UserName = "username";
client.ClientCredentials.UserName.Password = "bad password";
client.ChannelFactory.Faulted += new EventHandler(ChannelFactory_Faulted);
client.Open(); // This hangs for 01:00:00 minute
// Doesn't reach here
client.DoSomethingCompleted += new EventHandler<DoSomethingEventArgs(client_DoSomethingCompleted);
client.DoSomethingAsync(param);
}
catch (Exception ex)
{
// Enters here with a 'TimeoutException' exception
}
why does the client not trigger the 'Faulted' method I have ?
Why does it wait for a response from the service even though the service through a 'FaultException' during the 'Validate' method of the 'CustomUserNameValidator' ?
Sure, the code you are using appears to be missing 3 lines after your code line:
client.ChannelFactory.Faulted += new EventHandler(ChannelFactory_Faulted);
But again, I'm taking a shot in the dark since I've not made use of this option yet.
var local = client.ChannelFactory.CreateChannel();
((IClientChannel)local).Faulted += ChannelFaulted;
local.Open();
Better yet, the open method doesn't appear to be necessary according to the sample provide here: ChannelFactory
I personally have not used the ChannelFactory.Faulted event handler however, here is a post for your consideration: creating-wcf-channelfactory

WCF nested Callback

The backgound: I am trying to forward the server-side ApplyChangeFailed event that is fired by a Sync Services for ADO 1.0 DBServerSyncProvider to the client. All the code examples for Sync Services conflict resolution do not use WCF, and when the client connects to the server database directly, this problem does not exist. My DBServerSyncProvider is wrapped by a head-less WCF service, however, and I cannot show the user a dialog with the offending data for review.
So, the obvious solution seemed to be to convert the HTTP WCF service that Sync Services generated to TCP, make it a duplex connection, and define a callback handler on the client that receives the SyncConflict object and sets the Action property of the event.
When I did that, I got a runtime error (before the callback was attempted):
System.InvalidOperationException: This operation would deadlock because the
reply cannot be received until the current Message completes processing. If
you want to allow out-of-order message processing, specify ConcurrencyMode of
Reentrant or Multiple on CallbackBehaviorAttribute.
So I did what the message suggested and decorated both the service and the callback behavior with the Multiple attribute. Then the runtime error went away, but the call results in a "deadlock" and never returns. What do I do to get around this? Is it not possible to have a WCF service that calls back the client before the original service call returns?
Edit: I think this could be the explanation of the issue, but I am still not sure what the correct solution should be.
After updating the ConcurrencyMode have you tried firing the callback in a seperate thread?
This answer to another question has some example code that starts another thread and passes through the callback, you might be able to modify that design for your purpose?
By starting the sync agent in a separate thread on the client, the callback works just fine:
private int kickOffSyncInSeparateThread()
{
SyncRunner syncRunner = new SyncRunner();
Thread syncThread = new Thread(
new ThreadStart(syncRunner.RunSyncInThread));
try
{
syncThread.Start();
}
catch (ThreadStateException ex)
{
Console.WriteLine(ex);
return 1;
}
catch (ThreadInterruptedException ex)
{
Console.WriteLine(ex);
return 2;
}
return 0;
}
And this is my SyncRunner:
class SyncRunner
{
public void RunSyncInThread()
{
MysyncAgent = new MySyncAgent();
syncAgent.addUserIdParameter("56623239-d855-de11-8e97-0016cfe25fa3");
Microsoft.Synchronization.Data.SyncStatistics syncStats =
syncAgent.Synchronize();
}
}