WCF UnobservedTaskException
-
I do not understand why there is an UnobservedTaskException in that code at all... A method in a WCF service is defined as
\[OperationContract\] \[FaultContract(typeof(RemoteException))\] Task SetInfraredFocus(string \_clientName, double \_focus);
A WPF application calls out to it via:
public void SetFocus(double \_focus) { try { m\_RemoteManualPsControl.SetInfraredFocus(UtilsEnvironment.TerminalName, \_focus).Wait(); } catch (Exception ex) { Logger.LogException(Name, ex); } }
Note the
.Wait()
and thecatch
. That in turn calls the WCF client classpublic Task SetInfraredFocus(string \_clientName, double \_focus) { Task task = new Task(() => RemoteChannelProvider.Call(\_channel => \_channel.SetInfraredFocus(\_clientName, \_focus))); task.Start(); return task; }
True, I ought to add a CallAsync function to our WCF client base class, but that's quite convoluted code there... The server (a singleton service, multi-threaded) has a simple implementation for the sake of demonstration:
public async Task SetInfraredFocus(string \_clientName, double \_focus) { try { await Task.Delay(1); throw new Exception("Oh fuck!"); } catch (Exception ex) { Logger.LogException(Name, ex); RemoteException.ThrowFaultExceptionFromException(ex); } }
And then I get that UnobservedTaskException... Its detail clearly shows where it comes from: it is the RemoteException created from the "Oh fuck!" exception of the server. But why does the "catch" in the function on the top not catch the exception?
Oh sanctissimi Wilhelmus, Theodorus, et Fredericus!
-
I do not understand why there is an UnobservedTaskException in that code at all... A method in a WCF service is defined as
\[OperationContract\] \[FaultContract(typeof(RemoteException))\] Task SetInfraredFocus(string \_clientName, double \_focus);
A WPF application calls out to it via:
public void SetFocus(double \_focus) { try { m\_RemoteManualPsControl.SetInfraredFocus(UtilsEnvironment.TerminalName, \_focus).Wait(); } catch (Exception ex) { Logger.LogException(Name, ex); } }
Note the
.Wait()
and thecatch
. That in turn calls the WCF client classpublic Task SetInfraredFocus(string \_clientName, double \_focus) { Task task = new Task(() => RemoteChannelProvider.Call(\_channel => \_channel.SetInfraredFocus(\_clientName, \_focus))); task.Start(); return task; }
True, I ought to add a CallAsync function to our WCF client base class, but that's quite convoluted code there... The server (a singleton service, multi-threaded) has a simple implementation for the sake of demonstration:
public async Task SetInfraredFocus(string \_clientName, double \_focus) { try { await Task.Delay(1); throw new Exception("Oh fuck!"); } catch (Exception ex) { Logger.LogException(Name, ex); RemoteException.ThrowFaultExceptionFromException(ex); } }
And then I get that UnobservedTaskException... Its detail clearly shows where it comes from: it is the RemoteException created from the "Oh fuck!" exception of the server. But why does the "catch" in the function on the top not catch the exception?
Oh sanctissimi Wilhelmus, Theodorus, et Fredericus!
Bernhard Hiller wrote:
Task task = new Task(() => RemoteChannelProvider.Call(_channel => _channel.SetInfraredFocus(_clientName, _focus)));
That line looks suspicious to me. The
RemoteChannelProvider
doesn't seem to be a built-in class. What's the signature of theCall
method? What I suspect is happening is this:SetInfraredFocus
returns aTask
;Call
may or may not discard that task;- Even if it doesn't discard the task, the
new Task
constructor definitely discards it - none of the overloads accept aFunc<Task>
; - The task returned from the client therefore doesn't observe the result of the task returned from the server. It could even complete before the server call has finished.
If the
RemoteChannelProvider.Call
method returns theTask
, then you should be able to fix this by replacing the client code with:public Task SetInfraredFocus(string _clientName, double _focus)
{
return Task.Run(() => RemoteChannelProvider.Call(_channel => _channel.SetInfraredFocus(_clientName, _focus)));
}Task.Run
has overloads which accept aFunc<Task>
. The task returned from these will not complete until the inner task has completed, and it will propagate any exceptions correctly.
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
-
Bernhard Hiller wrote:
Task task = new Task(() => RemoteChannelProvider.Call(_channel => _channel.SetInfraredFocus(_clientName, _focus)));
That line looks suspicious to me. The
RemoteChannelProvider
doesn't seem to be a built-in class. What's the signature of theCall
method? What I suspect is happening is this:SetInfraredFocus
returns aTask
;Call
may or may not discard that task;- Even if it doesn't discard the task, the
new Task
constructor definitely discards it - none of the overloads accept aFunc<Task>
; - The task returned from the client therefore doesn't observe the result of the task returned from the server. It could even complete before the server call has finished.
If the
RemoteChannelProvider.Call
method returns theTask
, then you should be able to fix this by replacing the client code with:public Task SetInfraredFocus(string _clientName, double _focus)
{
return Task.Run(() => RemoteChannelProvider.Call(_channel => _channel.SetInfraredFocus(_clientName, _focus)));
}Task.Run
has overloads which accept aFunc<Task>
. The task returned from these will not complete until the inner task has completed, and it will propagate any exceptions correctly.
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
That overload does not do the trick either... The "RemoteChannelProvider" is not a built-in class. The signature is:
public delegate void CallProxyDelegate(T _channel);
...
void Call(CallProxyDelegate _codeBlock, [CallerMemberName] string _functionName = "");Some implementation details:
m_ConfigurationChannelFactory = new ConfigurationDuplexChannelFactory(m_CallbackObject, ConfigurationName, null, config);
...
m_CommunicationChannel = (ICommunicationObject)m_ConfigurationChannelFactory.CreateChannel();
...
_codeBlock((T)m_CommunicationChannel);I do not know how that handles a Task.
Oh sanctissimi Wilhelmus, Theodorus, et Fredericus!
-
That overload does not do the trick either... The "RemoteChannelProvider" is not a built-in class. The signature is:
public delegate void CallProxyDelegate(T _channel);
...
void Call(CallProxyDelegate _codeBlock, [CallerMemberName] string _functionName = "");Some implementation details:
m_ConfigurationChannelFactory = new ConfigurationDuplexChannelFactory(m_CallbackObject, ConfigurationName, null, config);
...
m_CommunicationChannel = (ICommunicationObject)m_ConfigurationChannelFactory.CreateChannel();
...
_codeBlock((T)m_CommunicationChannel);I do not know how that handles a Task.
Oh sanctissimi Wilhelmus, Theodorus, et Fredericus!
OK, then it's definitely throwing the
Task
away. How does theRemoteChannelProvider
class cope with server methods which return a value? If it doesn't, you might still be able to hack around it:public Task SetInfraredFocus(string _clientName, double _focus)
{
return Task.Run(delegate
{
Task result = null;
RemoteChannelProvider.Call(_channel => result = _channel.SetInfraredFocus(_clientName, _focus));
return result;
});
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
-
OK, then it's definitely throwing the
Task
away. How does theRemoteChannelProvider
class cope with server methods which return a value? If it doesn't, you might still be able to hack around it:public Task SetInfraredFocus(string _clientName, double _focus)
{
return Task.Run(delegate
{
Task result = null;
RemoteChannelProvider.Call(_channel => result = _channel.SetInfraredFocus(_clientName, _focus));
return result;
});
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
Perfect! That hack really works. (But I still will need some time to find out how it works...) :thumbsup:
Oh sanctissimi Wilhelmus, Theodorus, et Fredericus!