I would like my Silverlight client to be able to display exceptions that have happened at the server during a WCF call.
Given my current code to create a WCF Channel (on the client):
// create the binding elements
BinaryMessageEncodingBindingElement binaryMessageEncoding = new BinaryMessageEncodingBindingElement();
HttpTransportBindingElement httpTransport = new HttpTransportBindingElement() { MaxBufferSize = int.MaxValue, MaxReceivedMessageSize = int.MaxValue };
// add the binding elements into a Custom Binding
CustomBinding customBinding = new CustomBinding(binaryMessageEncoding, httpTransport);
// create the Endpoint URL
EndpointAddress endpointAddress = new EndpointAddress(serviceUrl);
// create an interface for the WCF service
ChannelFactory<TWcfApiEndPoint> channelFactory=new ChannelFactory<TWcfApiEndPoint>(customBinding, endpointAddress);
channelFactory.Faulted += new EventHandler(channelFactory_Faulted);
TWcfApiEndPoint client = channelFactory.CreateChannel();
return client;
When an exception occurs, I just get a "NotFound" exception, which is obviously of no use. How can I get the exception information?
I use this code to use the client object returned above:
try
{
// customFieldsBroker is the client returned above
customFieldsBroker.BeginCreateCustomField(DataTypeID, newCustomField, (result) =>
{
var response = ((ICustomFieldsBroker)result.AsyncState).EndCreateCustomField(result);
}, customFieldsBroker);
}
catch (Exception ex)
{
// would like to handle exception here
}
Wrapping the Begin/End calls in a try { } catch { } block doesn't seem to even jump into the catch { } block.
If it matters, I'm using Silverlight 3 at the client.
Due to security limitations in the browser sandbox, silverlight can't see the body of server errors (status code 500). To get this working you need to make a change to the server side, to change the way it returns faults to the browser. There's an MSDN article that describes it in detail.
You need to do two things:
declare the Fault exception as part of the contract
throw the exception as a fault exception
[OperationContract]
[FaultContract(typeof(ArithmeticFault))]
public int Calculate(Operation op, int a, int b)
{
// ...
}
throw new FaultException();
Related
I have a WCF request in WP8 environment that I wrapped according to this
http://msdn.microsoft.com/en-us/library/hh873178%28v=vs.110%29.aspx#EAP
My call to the WCF service proceeds as follows:
try
{
var result = await mWCFClient.PerformRequestAsync();
}
catch(Exception e)
{
}
where PerformRequestAsync is an extension method. i.e.
public static ResultType PerformRequestAsync(this WCFClient client)
{
// EAP wrapper code
}
What happens is that occasionally something goes wrong on the WCF service and it returns "NotFound". I am not 100% sure why this happens and it seems like a rare occasion. The problem, however, is not the WCF service behavior, but the fact that it breaks in the EndPerformRequestAsync() in the automatically generated WCF code instead of going to my exception handler.
How and where should I be catching this exception as it never reaches my intended handler?!
[Edit]
As per Stephen's request, I've included the wrapper code here:
public static Task<RegistrationResult> RegisterAsync(this StoreServiceReference.StoreServiceClient client, string token, bool dummy)
{
var tcs = new TaskCompletionSource<RegistrationResult>();
EventHandler<RegisterCompletedEventArgs> handler = null;
handler = (_, e) =>
{
client.RegisterCompleted -= handler;
if (e.Error != null)
tcs.TrySetException(e.Error);
else if (e.Cancelled)
tcs.TrySetCanceled();
else
tcs.TrySetResult(e.Result);
};
client.RegisterCompleted += handler;
PerformStoreRequest(client, () => client.RegisterAsync(), token);
return tcs.Task;
}
private static void PerformStoreRequest(StoreServiceClient client, Action action, string token)
{
using (new OperationContextScope(client.InnerChannel))
{
HttpRequestMessageProperty requestMessage = new HttpRequestMessageProperty();
requestMessage.Headers[STORE_TOKEN_HTTP_HEADER] = token;
OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = requestMessage;
action.Invoke();
// TODO: Do we need to add handler here?
}
}
Now that I look at it, I think the problem stems from the nature of action invoke. But adding custom headers to WP8 WCF services already is a pain.
The action inside is an async operation, but Invoke as far as I know is not.
What's the proper way to go about it here?
I configured my WCF Data Service this way:
public static void InitializeService(DataServiceConfiguration config)
{
config.UseVerboseErrors = true;
In the HandleException method I set this:
protected override void HandleException(HandleExceptionArgs args)
{
args.UseVerboseErrors = true;
I added this attribute to my service class:
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public class InformationService: DataService<InformationEntities>
On the client side (Android) I get this message from the server:
An error occurred while processing this request.
Where is the detailed exception? What else should I set?
You might try adding this to your WCF service code at the startup on the host. It causes exception details to be included in faults that are thrown to, and caught by, the client. Without this, details of the exceptions are hidden.
And make sure that, when you're catching those internal exceptions, that you throw a generic "fault" object to the client using the WCF System.ServiceModel.CommunicationObjectFaultedException object. Dot NET exceptions are Microsoft centric, so if you're throwing errors to non-MS devices like Android or iPhone, you need to use the CommunicationObjectFaultedException.
ServiceDebugBehavior debug = ServiceHost.Description.Behaviors.Find<ServiceDebugBehavior>();
if (debug == null) {
ServiceHost.Description.Behaviors.Add(new ServiceDebugBehavior { IncludeExceptionDetailInFaults = true });
} else {
if (!debug.IncludeExceptionDetailInFaults) {
debug.IncludeExceptionDetailInFaults = true;
}
}
I have the following planned architecture for my WCF client library:
using ChannelFactory instead of svcutil generated proxies because
I need more control and also I want to keep the client in a separate
assembly and avoid regenerating when my WCF service changes
need to apply a behavior with a message inspector to my WCF
endpoint, so each channel is able to send its
own authentication token
my client library will be used from a MVC front-end, so I'll have to think about possible threading issues
I'm using .NET 4.5 (maybe it has some helpers or new approaches to implement WCF clients in some better way?)
I have read many articles about various separate bits but I'm still confused about how to put it all together the right way. I have the following questions:
as I understand, it is recommended to cache ChannelFactory in a static variable and then get channels out of it, right?
is endpoint behavior specific to the entire ChannelFactory or I can apply my authentication behavior for each channel separately? If the behavior is specific to the entire factory, this means that I cannot keep any state information in my endpoint behavior objects because the same auth token will get reused for every channel, but obviously I want each channel to have its own auth token for the current user. This means, that I'll have to calculate the token inside of my endpoint behavior (I can keep it in HttpContext, and my message inspector behavior will just add it to the outgoing messages).
my client class is disposable (implements IDispose). How do I dispose the channel correctly, knowing that it might be in any possible state (not opened, opened, failed ...)? Do I just dispose it? Do I abort it and then dispose? Do I close it (but it might be not opened yet at all) and then dispose?
what do I do if I get some fault when working with the channel? Is only the channel broken or entire ChannelFactory is broken?
I guess, a line of code speaks more than a thousand words, so here is my idea in code form. I have marked all my questions above with "???" in the code.
public class MyServiceClient : IDisposable
{
// channel factory cache
private static ChannelFactory<IMyService> _factory;
private static object _lock = new object();
private IMyService _client = null;
private bool _isDisposed = false;
/// <summary>
/// Creates a channel for the service
/// </summary>
public MyServiceClient()
{
lock (_lock)
{
if (_factory == null)
{
// ... set up custom bindings here and get some config values
var endpoint = new EndpointAddress(myServiceUrl);
_factory = new ChannelFactory<IMyService>(binding, endpoint);
// ???? do I add my auth behavior for entire ChannelFactory
// or I can apply it for individual channels when I create them?
}
}
_client = _factory.CreateChannel();
}
public string MyMethod()
{
RequireClientInWorkingState();
try
{
return _client.MyMethod();
}
catch
{
RecoverFromChannelFailure();
throw;
}
}
private void RequireClientInWorkingState()
{
if (_isDisposed)
throw new InvalidOperationException("This client was disposed. Create a new one.");
// ??? is it enough to check for CommunicationState.Opened && Created?
if (state != CommunicationState.Created && state != CommunicationState.Opened)
throw new InvalidOperationException("The client channel is not ready to work. Create a new one.");
}
private void RecoverFromChannelFailure()
{
// ??? is it the best way to check if there was a problem with the channel?
if (((IChannel)_client).State != CommunicationState.Opened)
{
// ??? is it safe to call Abort? won't it throw?
((IChannel)_client).Abort();
}
// ??? and what about ChannelFactory?
// will it still be able to create channels or it also might be broken and must be thrown away?
// In that case, how do I clean up ChannelFactory correctly before creating a new one?
}
#region IDisposable
public void Dispose()
{
// ??? is it how to free the channel correctly?
// I've heard, broken channels might throw when closing
// ??? what if it is not opened yet?
// ??? what if it is in fault state?
try
{
((IChannel)_client).Close();
}
catch
{
((IChannel)_client).Abort();
}
((IDisposable)_client).Dispose();
_client = null;
_isDisposed = true;
}
#endregion
}
I guess better late then never... and looks like author has it working, this might help future WCF users.
1) ChannelFactory arranges the channel which includes all behaviors for the channel. Creating the channel via CreateChannel method "activates" the channel. Channel factories can be cached.
2) You shape the channel factory with bindings and behaviors. This shape is shared with everyone who creates this channel. As you noted in your comment you can attach message inspectors but more common case is to use Header to send custom state information to the service. You can attach headers via OperationContext.Current
using (var op = new OperationContextScope((IContextChannel)proxy))
{
var header = new MessageHeader<string>("Some State");
var hout = header.GetUntypedHeader("message", "urn:someNamespace");
OperationContext.Current.OutgoingMessageHeaders.Add(hout);
}
3) This is my general way of disposing the client channel and factory (this method is part of my ProxyBase class)
public virtual void Dispose()
{
CloseChannel();
CloseFactory();
}
protected void CloseChannel()
{
if (((IChannel)_client).State == CommunicationState.Opened)
{
try
{
((IChannel)_client).Close();
}
catch (TimeoutException /* timeout */)
{
// Handle the timeout exception
((IChannel)innerChannel).Abort();
}
catch (CommunicationException /* communicationException */)
{
// Handle the communication exception
((IChannel)_client).Abort();
}
}
}
protected void CloseFactory()
{
if (Factory.State == CommunicationState.Opened)
{
try
{
Factory.Close();
}
catch (TimeoutException /* timeout */)
{
// Handle the timeout exception
Factory.Abort();
}
catch (CommunicationException /* communicationException */)
{
// Handle the communication exception
Factory.Abort();
}
}
}
4) WCF will fault the channel not the factory. You can implement a re-connect logic but that would require that you create and derive your clients from some custom ProxyBase e.g.
protected I Channel
{
get
{
lock (_channelLock)
{
if (! object.Equals(innerChannel, default(I)))
{
ICommunicationObject channelObject = innerChannel as ICommunicationObject;
if ((channelObject.State == CommunicationState.Faulted) || (channelObject.State == CommunicationState.Closed))
{
// Channel is faulted or closing for some reason, attempt to recreate channel
innerChannel = default(I);
}
}
if (object.Equals(innerChannel, default(I)))
{
Debug.Assert(Factory != null);
innerChannel = Factory.CreateChannel();
((ICommunicationObject)innerChannel).Faulted += new EventHandler(Channel_Faulted);
}
}
return innerChannel;
}
}
5) Do not re-use channels. Open, do something, close is the normal usage pattern.
6) Create common proxy base class and derive all your clients from it. This can be helpful, like re-connecting, using pre-invoke/post invoke logic, consuming events from factory (e.g. Faulted, Opening)
7) Create your own CustomChannelFactory this gives you further control how factory behaves e.g. Set default timeouts, enforce various binding settings (MaxMessageSizes) etc.
public static void SetTimeouts(Binding binding, TimeSpan? timeout = null, TimeSpan? debugTimeout = null)
{
if (timeout == null)
{
timeout = new TimeSpan(0, 0, 1, 0);
}
if (debugTimeout == null)
{
debugTimeout = new TimeSpan(0, 0, 10, 0);
}
if (Debugger.IsAttached)
{
binding.ReceiveTimeout = debugTimeout.Value;
binding.SendTimeout = debugTimeout.Value;
}
else
{
binding.ReceiveTimeout = timeout.Value;
binding.SendTimeout = timeout.Value;
}
}
I have a project where I should write WCF service that calls another WCF service. It looks as following:
[ServiceContract]
public interface IAsurService
{
[OperationContract(ReplyAction = AsurService.ReplyAction_GetCatalogList)]
Message GetCatalogList();
public Message GetCatalogList()
{
// The external client service
GetNsiClient client = new GetNsiClient();
authContext auth = new authContext
{
company = "asur_nsi",
password = "lapshovva",
user = "dogm_LapshovVA"
};
catalogs catalogs = client.getCatalogList(auth);
How can I handle errors in this case? Can I use standard fault contract approach like this:
[DataContract]
public class AsurDataFaultException
{
private string reason;
[DataMember]
public string Reason
{
get { return reason; }
set { reason = value; }
}
}
public Message GetCatalogList()
{
// The external client service
GetNsiClient client = new GetNsiClient();
authContext auth = new authContext
{
company = "asur_nsi",
password = "lapshovva",
user = "dogm_LapshovVA"
};
catalogs catalogs = null;
try
{
catalogs = client.getCatalogList(auth);
}
catch (Exception exception)
{
AsurDataFaultException fault = new AsurDataFaultException();
fault.Reason = "The error: " + exception.Message.ToString();
throw new FaultException<AsurDataFaultException>(fault);
}
Or something else?
Thank you in advance.
Goran
If the remote WCF service's failure causes a failure of your code, then obviously you should throw a fault but that fault should not expose the inner details of the remote WCF service or its exception, like so:
catch (Exception exception)
{
// Indicate all that you may expose -- that the data is unavailable.
DataUnavailableFault fault = new DataUnavailableFault();
throw new FaultException<DataUnavailableFault>(fault);
}
You may of course also use a retry mechanism, but obviously if what you end up with is a failure in the remote service, you should indicate that to your caller by throwing an appropriate fault.
I use WCF for a client server system.
When I add a service reference to IService on the server, a proxy class ServiceClient is generated.
My code looks like the following:
ServiceClient client = new ServiceClient();
try
{
client.Operation1();
}
catch(Exception ex)
{
// Handle Exception
}
try
{
client.Operation2();
}
catch(Exception ex)
{
// Handle Exception
}
The problem is that if there is a communication exception in the first call, the client's state changes to Faulted, and I don't know how to reopen it to make the second call. Is there a way to reopen it? or should I create a new one and replace the instance (It doesn't seem like an elegant way)?
Once a an ICommunicationObject (your WCF client object) is in a faulted state, the only way to "re-open" it is to create a new one.
ServiceClient client = new ServiceClient();
try
{
client.Operation1();
}
catch(Exception ex)
{
if (client.State == CommunicationState.Faulted)
{
client.Abort();
client = new ServiceClient();
}
}
try
{
client.Operation2();
}
catch(Exception ex)
{
// Handle Exception
}
If there is a communication exception on the first call which is causing a faulted state, you have to basically "re-create" the WCF client proxy. In your example I would probably do something like:
if (client.State == CommunicationState.Faulted)
client = new ServiceClient();
This would allow you to "re-open" the connection if it is faulted. It may seem a little overkill, but if you're getting a communication exception on the client side, there's probably something else going on (i.e.: server dead? server not responding?)
Good luck
Agree with the last answers, once failed, you need to abort. We use a combination of lambdas and a method like the following to do this:
public static void Use<TServiceInterface>(TServiceInterface proxy, Action handler)
{
Type proxyType = typeof(TServiceInterface);
IClientChannel channel = (IClientChannel)proxy;
try
{
handler();
_logSource.Log(LogLevel.Debug, string.Format("Closing client channel for '{0}' ...", proxyType.Name));
channel.Close();
_logSource.Log(LogLevel.Debug, string.Format("Client channel for '{0}' closed.", proxyType.Name));
}
catch
{
if (channel.State == CommunicationState.Faulted)
{
_logSource.Log(LogLevel.Debug, string.Format("Aborting client channel for '{0}' ...", proxyType.Name));
channel.Abort();
_logSource.Log(LogLevel.Debug, string.Format("Client channel for '{0}' aborted.", proxyType.Name));
}
else
{
_logSource.Log(LogLevel.Debug, string.Format("Closing client channel for '{0}' ...", proxyType.Name));
channel.Close();
_logSource.Log(LogLevel.Debug, string.Format("Client channel for '{0}' closed.", proxyType.Name));
}
throw;
}
}
This is a slight modification of a solution that is already on the .net, but it works great for handling proxies. You then can put multiple service calls in the same lambda expression, and pass it into the method.
This is most likely caused by an unhandled exception on the server side. WCF runtime by default terminates your service instance and puts the channel in faulted state in case of unhandled exception and you can no longer communicate over that channel. So you will need to establish a new session with the service. You should catch exceptions on the server side and send the soap faults by raising FaultException or defining FaultContract. There is also returnUnknownExceptionsAsFaults service behavior that you can use.