Awaiting a service task gets TaskCanceledException: A task was canceled - c#

I have one app (UWP - Win10) and a Windows service.
The service is running in background, and they were both developed in C#. "callAsync" is the method on the service. I am using await to call it on the client.
var obj = await callAsync(10);
The problem is:
If this call takes less than 1min40s (100 seconds), then everything works ok. But if it takes more than 1min40s, then an exception will occur "TaskCanceledException: A task was canceled".
I have search SO and the web but still could not find any indication on how to resolve this "timeout" issue. I have added all the "open/close/receive/send" timeout flags on both app and service app.config, although the exception that is thrown in that case is different.
If I try with a simple delay in the client:
await Task.delay(200000);
it works properly.
This service was added through VS2015 "Add Service Reference". I have also "attached" to the server and the server keeps running and prints in the console before and after logs (to confirm that everything is ok).
What am I missing? What configuration and where do I need to change so that the task can run for more than 1 minute and 40 seconds?
CODE:
Example of Server Pseudo-Code:
Interface File:
[ServiceContract(Namespace="http://.....")]
interface ICom {
[OperationContract]
int call(int val);
}
Service.cs
public ServiceHost serviceHost = null;
public BlaBlaWindowsService()
{
ServiceName = "BlaBlaWindowsService";
}
public static void Main()
{
ServiceBase.Run(new BlaBlaWindowsService());
}
protected override void OnStart(string[] args)
{
if (serviceHost != null)
{
serviceHost.Close();
}
serviceHost = new ServiceHost(typeof(BlaBlaService));
serviceHost.Open();
}
protected override void OnStop()
{
if (serviceHost != null)
{
serviceHost.Close();
serviceHost = null;
}
}
}
[RunInstaller(true)]
public class ProjectInstaller : Installer
{
private ServiceProcessInstaller process;
private ServiceInstaller service;
public ProjectInstaller()
{
process = new ServiceProcessInstaller();
process.Account = ServiceAccount.LocalSystem;
service = new ServiceInstaller();
service.ServiceName = "BlaBlaWindowsService";
Installers.Add(process);
Installers.Add(service);
}
}
BlaBlaService.cs
class TPAService : ITPAComunication {
public int call(int val) {
System.Threading.Thread.Sleep(200000)
return 0;
}
}
App.config file:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<binding name="ServiceTimeout" closeTimeout="00:10:00" receiveTimeout="00:10:00" openTimeout="00:10:00" sendTimeout="00:10:00"/>
</bindings>
<services>
<service name="BlaBla.Service.Service"
behaviorConfiguration="ServiceBehavior">
<host>
<baseAddresses>
<add baseAddress="http://localhost:8000/BlaBla/service"/>
</baseAddresses>
</host>
<endpoint address=""
binding="basicHttpBinding"
bindingConfiguration="ServiceTimeout"
contract="BlaBla.Service.ICom" />
<endpoint address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="False"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
Example of App pseudo-code:
System.ServiceModel.EndpointAddress epa = new System.ServiceModel.EndpointAddress("http://localhost:8000/blabla/service");
System.ServiceModel.BasicHttpBinding bhb = new System.ServiceModel.BasicHttpBinding();
Timespan t = new TimeSpan(0, 10, 0);
bhb.SendTimeout = t; bhb.ReceiveTimeout =t; bhb.OpenTimeout = t; bhb.CloseTimeout = t;
Blabla.ComunicationClient com = new Blabla.ComunicationClient(bhb, epa);
var obj = await com.callAsync(int val);
return obj;
UPDATE #1
This situation only happens in UWP. I have created a similar WinForms project and everything works as expected. This means that it is probably something related to UWP.

After several tries, manipulating different config files, I have not found a solution regarding how to remove the timeout limitation of 100 seconds. To solve this specific problem, I implemented a counter-measure.
What I have found during my tries was:
If the project is in WinForms, everything works as expected. This means, this 100-second-limit, is an UWP "Feature";
If you reduce the SendTimeout to less than 100 seconds, it will throw a TimeoutException with the corresponding timer;
This is not exclusive to the Windows Service. It also happens when comunicating with a SOAP Webservice implementation;
This seems to happen only if you are doing a task the requires "external communication" with a service reference. If you have a "internal" task that takes more than 100 seconds, it works as expected (eg. await Task.delay(250000)).
How I solved this?
After chatting at C# SO channel, #Squiggle suggested a polling approach and that is what I implemented and tested successfully.
These are the steps I took:
I updated the existing service request (call(int val)) to accept a another argument, a Guid, so I could identify which request I wanted to do the "polling";
I created an additional request at the service to InquireAboutCurrentRequest that accepted also accepted a GUID parameter, and returned an int;
I updated the Service Reference at the UWP app with the new request;
I called "await call(val,guid)" with a try catch. I did this because 90% of these calls return in less than 30 seconds*;
In the catch I added an "If" that checked if the exception was a CancelTask, and if was I call "await InquireAboutCurrentRequest(guid)";
This method, at the windows service, keeps checking if the other operation has ended and sleeps every X seconds. Since the total time of the first call can only be at most 2 minutes, I only need to wait 20 seconds;
After that I will deal with the result accordingly, but at least this time I know I have "waited 2 minutes" for the response.
There are other possible solutions such as sockets, which I have not tried, that could work.
* If all the requests take more than 100 seconds, I suggest using the polling approach from the beginning of the requests, instead of waiting for the try/catch.

I'm not sure exactly - but may be better use Task.WhenAll instead of await ?

Related

dynamically set the base address of a WCF service in the client

I built a WCF service library and hosted it through a host application. Then I constructed a client application, but it seems that the address of the service host is hard coded in the client program. What if the host changes its address? Is it possible to write the client application so that the address of the host can be entered by the client at run time?
Yes, it's possible, if you write the WCF client proxy by hand, instead of generating it automatically with Visual Studio adding a service reference.
Let's start from this example (https://learn.microsoft.com/it-it/dotnet/framework/wcf/feature-details/how-to-use-the-channelfactory), just to understand how ChannelFactory works, and then modify it a little bit, adding the following function.
private ChannelFactory<IMath> _myChannelFactory;
// ...
private IMath GetChannel(string endpointConfigurationName, string endpointAddress)
{
if (_myChannelFactory == null)
{
this.DebugLog("Channel factory is null, creating new one");
if (String.IsNullOrEmpty(endpointAddress))
{
_myChannelFactory = new ChannelFactory<IMath>(endpointConfigurationName);
}
else
{
_myChannelFactory = new ChannelFactory<IMath>(endpointConfigurationName, new EndpointAddress(endpointAddress));
}
}
return _myChannelFactory.CreateChannel();
}
You can define the default server IP in the client App.config file
<system.serviceModel>
<!-- ... -->
<client>
<endpoint address="net.tcp://192.168.10.55:81/math/" binding="netTcpBinding"
bindingConfiguration="NetTcpBinding_IMath"
contract="MyNamespace.IMath" name="NetTcpBinding_IMath" />
</client>
</system.serviceModel>
In this way, when GetChannel("NetTcpBinding_IMath", "net.tcp://127.0.0.1:81/math") is called, it picks up the endpoint configuration from App.config file, replacing the default address (192.168.10.55) with the new one (127.0.0.1).
Some more documentation about ChannelFactory: https://learn.microsoft.com/en-us/dotnet/api/system.servicemodel.channelfactory-1.createchannel?view=netframework-4.8

.NET Core 2.1 / Angular - SignalR takes exactly 2 minutes to connect to hub

I have an Ionic app and I want it to connect to my socket. This worked in the SignalR preview just fine, and it essensialy still does, but it takes 2 minutes to connect for some reason...
I also get some errors when it connects:
This is my javascript code:
ngOnInit() {
const connection = new signalR.HubConnectionBuilder()
.withUrl("http://192.168.178.11:8040/socket?sessionId=3dc1dc11")
.build();
connection.on("ReceiveMessage", message => {
console.log(message);
this.zone.run(() => {
if(this.locked == true) {
this.locked = false
} else {
this.locked = true;
}
});
console.log(this.locked);
});
connection.start().catch(err => console.error);
}
This is my Hub:
public class DeviceHub : Hub
{
public override Task OnConnectedAsync()
{
var sessionId = Context.GetHttpContext().Request.Query["sessionId"];
return Groups.AddToGroupAsync(Context.ConnectionId, sessionId);
}
}
And this is my configuration in Startup.cs:
app.UseSignalR(routes =>
{
routes.MapHub<DeviceHub>("/socket");
});
Now my question is: How do I solve this?
EDIT 1:
The delay is before the OnConnectedAsync() method is invoked.
EDIT 2:
One more thing I think I should add is that it directly does a request to my API:
ws://192.168.178.11:8040/socket?sessionId=3dc1dc11&id=Pt-JDlSPq2_WEIl-8cdPZA
And that's the request that takes exactly two minutes to finish.
EDIT 3:
One more thing I would like to point out is that I am running a proxy. I cannot connect directy from my phone to the API, so I use a proxy for it. I have no idea if this has anything to do with it, but I just wanted to point it out just in case.
Well...
Shame on me.
The proxy caused the problem.
I have added bindings for my own IP in Visual Studio and it works now.
To do this open \APPLICATION_FOLDER\.vs\config\applicationhost.config.
Then look for <bindings> and add your own.
Example:
<bindings>
<binding protocol="http" bindingInformation="*:63251:localhost" />
<binding protocol="https" bindingInformation="*:44333:localhost" />
<binding protocol="http" bindingInformation="*:63251:192.168.178.11" />
<binding protocol="https" bindingInformation="*:44333:192.168.178.11" />
</bindings>
Save it, and you're done.
Note:
From now on you have to launch Visual Studio as administrator otherwise your application won't launch.

How to check wcf service is connected with client

I have created a WCF service in c#. Iam able to access it form a client app with url http://localhost:8080/classname/function
Is there any way to detect whether the client is connected from WCF service in which the client has the url http://localhost:8080 alone without class name and function name where the WCF service is listening to 8080 port at localhost?
Based upon your request to explain how to send an empty string message to a WCF service I have developed a VS2017 solution and uploaded onto the GITHUB for you.
The solution contain 3 projects, 2 on the WCF Service side (Class library and Console Application) and 1 for client.
WCF Service Side
First we define a ServiceContract that will have single OperationalContract to receive the string message and returns a boolean:
[ServiceContract]
public interface IClientConnectionService
{
[OperationContract]
bool Connect(string message);
}
Next, we have a class that implements this ServiceContract
public class ClientConnectionService : IClientConnectionService
{
public bool Connect(string message)
{
/*
* As per your comment on http://stackoverflow.com/questions/43366101/how-to-check-wcf-service-is-connected-with-client?noredirect=1#comment74005120_43366101
* the message should be empty, however you can pass string.
* Once you are done with processing you can return true or false depending upon how you want to carry out
* this operation.
*/
return true;
}
}
Next, we have the WCF service host manager (a console based application just to host this WCF service)
class Program
{
static void Main(string[] args)
{
using (ServiceHost host = new ServiceHost(typeof(ClientConnectionService)))
{
host.Open();
Console.WriteLine($"{host.Description.Name} is up and listening on the URI given below. Press <enter> to exit.");
PrintServiceInfo(host.Description);
Console.ReadLine();
}
}
private static void PrintServiceInfo(ServiceDescription desc)
{
foreach (ServiceEndpoint nextEndpoint in desc.Endpoints)
{
Console.WriteLine(nextEndpoint.Address);
}
}
}
Its responsibility is to just keep the WCF service listening for incomming requests on a net.tcp port defined in the config file:
<system.serviceModel>
<services>
<service name="StackOverflow.Wcf.Services.ClientConnectionService">
<endpoint
address="net.tcp://localhost:9988/ClientConnectionService/"
binding="netTcpBinding"
contract="StackOverflow.Wcf.Services.Contracts.IClientConnectionService"
></endpoint>
</service>
</services>
</system.serviceModel>
Once this is completed we have a running WCF service. Now lets turn our attention to the client that will consume this service.
WCF Client Side
This is just a console application that has a reference of WCF Service and it creates the proxy class to call the method on the service.
public class ClientConnectionServiceProxy : ClientBase<IClientConnectionService>
{
public bool Connect(string message)
{
return base.Channel.Connect(message);
}
}
Notice that we have used IClientConnectionService interface / contract from the service side. ClientBase<T> is a WCF Framework class.
Here is the program class that calls the WCF servie using above defined proxy class.
class Program
{
static void Main(string[] args)
{
using (ClientConnectionServiceProxy proxy = new ClientConnectionServiceProxy())
{
bool isCallSuccessful = proxy.Connect(string.Empty);
}
}
}
and here is the client configuration:
<system.serviceModel>
<client>
<endpoint
address="net.tcp://localhost:9988/ClientConnectionService/"
binding="netTcpBinding"
contract="StackOverflow.Wcf.Services.Contracts.IClientConnectionService"
></endpoint>
</client>
</system.serviceModel>
How to run:
Once you have downloaded the source code from GITHUB, open the StackOverflow.Wcf.sln file in VS2017 (thats what I have used to develop this - not sure if you can open it in the VS2015) and hit F5. You can put break points to step through the code and edit it as you wish.
Hope this makes it clear - leave any questions in the comments below.

RIA services WCF timeout

I have an application which is written in silverlight 3.0. It uses RIA services to communicate between the client and server.
My question doesn't seem to be answered very well on the web. The client communicates to the server using RIA services, which uses WCF behind the scenes. If the communication takes more than 60 seconds it times out with this message,
'Load operation failed for query 'ApplyUpgrade'. The HTTP requrest to 'http://localhost:52403/ClientBin/DatabaseUpgradeTool-Web-UpgradePackageDomainService.svc/binary' has exceeded the allotted timeout. The time allotted to this operation may have been a portion of a longer timeout.'
My server is performing a database upgrade, so it is valid for it to take more than 60 seconds. Probably double or triple that.
I tried settings like this in the web.config,
<services>
<service name="DatabaseUpgradeTool.Web.UpgradePackageDomainService">
<endpoint address="" binding="wsHttpBinding" contract="DatabaseUpgradeTool.Web.UpgradePackageDomainService"></endpoint>
<endpoint address="/soap" binding="basicHttpBinding" contract="DatabaseUpgradeTool.Web.UpgradePackageDomainService"></endpoint>
<endpoint address="/binary" binding="customBinding" bindingConfiguration="BinaryHttpBinding" contract="DatabaseUpgradeTool.Web.UpgradePackageDomainService"></endpoint>
</service>
</services>
<bindings>
<customBinding>
<binding name="BinaryHttpBinding"
receiveTimeout="00:00:10"
sendTimeout="00:00:10"
openTimeout="00:00:10"
closeTimeout="00:00:10">
<binaryMessageEncoding />
<httpTransport keepAliveEnabled="true"/>
</binding>
</customBinding>
</bindings>
Still no joy. Any ideas as to what is wrong with what I have tried above? I would expect the above to cause it to timeout within 10 seconds, not 60.
Thanks.
Not sure if this will help, I haven't tried it with time outs configurations, but it might point you in the right direction:
http://blogs.objectsharp.com/CS/blogs/dan/archive/2010/04/13/maxitemsinobjectgraph-wcf-ria-services-exception.aspx
I faced the same problem, I posted the answer to this question here: Silverlight 4 WCF RIA Service Timeout Problem
Here is the answer:
I'll explain my context and I wish it will work for my. I'm sure about that.
First of all to call RIA services, and using some domain context, in my example:
EmployeeDomainContext context = new EmployeeDomainContext();
InvokeOperation<bool> invokeOperation = context.GenerateTMEAccessByEmployee(1, 'Bob');
invokeOperation.Completed += (s, x) =>
{....};
Nothing new until here. And with this I was facing every time that same timeout exception after 1 minute. I spend quite a lot of time trying to face how to change the timeout definition, I tried all possible changes in Web.config and nothing. The solution was:
Create a CustomEmployeeDomainContext, that is a partial class localizated in the same path of the generated code and this class use the hook method OnCreate to change the behavior of created domain context. In this class you should wrote:
public partial class EmployeeDomainContext : DomainContext
{
partial void OnCreated()
{
PropertyInfo channelFactoryProperty = this.DomainClient.GetType().GetProperty("ChannelFactory");
if (channelFactoryProperty == null)
{
throw new InvalidOperationException(
"There is no 'ChannelFactory' property on the DomainClient.");
}
ChannelFactory factory = (ChannelFactory)channelFactoryProperty.GetValue(this.DomainClient, null);
factory.Endpoint.Binding.SendTimeout = new TimeSpan(0, 10, 0);
}
}
I looking forward for you feedback.

The underlying connection was closed: The connection was closed unexpectedly

This exception is consistently thrown on a SOAP Request which takes almost three minutes to receive and is 2.25 megs in size.
When scouring the web I find all sorts of posts which all seem to be about setting headers on the Request, some want me to not send the "Expect:" header, some want me to send the "Keep-Alive:" header, but irregardless of the headers I send I still get this pesky error. I don't believe that setting any headers is my answer, because I can recreate the exact same request using "curl" and a response does eventually come back with no problems what-so-ever.
My <httpRuntime maxRequestLength="409600" executionTimeout="900"/>.
I feel as if I'm running out of options. If anyone can provide any assistance I would be most grateful. A few other things to note would be that the server I'm Requesting data from is out of my hands, also these requests are over https and other requests with smaller responses work flawlessly.
Thanks
You tagged the post as .NET35, so are you using WCF?
If so, here is an example of the App.config we use for large data sets:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647">
<readerQuotas maxDepth="32" maxStringContentLength="8388608" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:1602/EndPoint.svc" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding" contract="IEndPointContract" name="EndPoint" behaviorConfiguration="EndpointBehaviour" />
</client>
<behaviors>
<endpointBehaviors>
<behavior name="EndpointBehaviour">
<dataContractSerializer maxItemsInObjectGraph="2147483647" />
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
I hope it's not too late for answering this question.
Try adding the following attribute on the definition of your contract interface: [ServiceKnownType(typeof(ReturnClass))]
For more generic solution that allows returning polymorphic classes please refer to this post:
http://www.goeleven.com/blog/entryDetail.aspx?entry=45
If you are using dbml instead of edmx you will get this( The underlying connection was closed: The connection was closed unexpectedly.) as dbml will not return serialisable data it needs datacontract so go to properties of dbml file and change the Serialization mode to unidirectional.
Have you tried the sugestion of this Blog Post? The problem will most probably lie in the TCP/HTTP stack implementation of .NET .
i got this error because my datatransfereobjects refered to each other in an recursive manner.
For example:
Customer-> (has) -> Rating
Rating-> (belong to) -> Customer
so you have to remove cycles.
[DataContract]
public class Rating
{
private Customer _customer;
//[DataMember] // <- EITHER HERE
public Customer Customer
{
get { return _customer; }
set { _customer = value; }
}
}
[DataContract]
public class Customer
{
private long _customerID;
[DataMember]
public long CustomerID
{
get { return _customerID; }
set { _customerID = value; }
}
[DataMember] // <- OR HERE
public Rating Rating
{
get { return _rating; }
set { _rating = value; }
}
}
Tried several ways to get rid of this error message until I found this solution:
http://kiranpatils.wordpress.com/2008/09/22/the-underlying-connection-was-closed-the-connection-was-closed-unexpectedly-while-returning-data-table-from-wcf-service/
You may change your List<> to DataSet. I suspect DataSet can handle much amount of data than the List<>.
Hope it helps.
I've got the same issue, and after deep investigations I found this article:
Merrick Chaffer's Blog
It was all related to setting the "dataContractSerializer" for both client and server.
Hope this to be helpful.
I have added another field, but didn't have a set on the property.
That was my solution for the same error.
[DataMember]
public bool HasValue
{
get { return true; }
set { }//adding this line made the solution.
}
This is a generic error raised if there is an internal error.
Try adding tracing here: http://msdn.microsoft.com/en-us/library/ms732023(v=vs.110).aspx
You will see the full log then.
For WCF with EF, just add the following code in the context class.
base.Configuration.ProxyCreationEnabled = false;

Categories