I have a WCF service deployed on two or more remote machines and there is a desktop based application that is used by the client to access any wcf service.
The WCF service is connected to SQL server 2005 to read and write data.
This is an intranet scenario in which the client should be on same domain.
Now there can be scenarios where the wcf service throws exceptions:
Invalid URL
WCF service is down
SQL server 2005 is not running
Client is not on the same domain
Authentication fails
Authorization fails
and many other exceptions.
For every exception I have to perform some action or update a status bar, depending on the exception. For example if authorization fails I have to prompt the user to re-enter their credentials.
Please suggest the best design approach to handle this.
You can definitely catch and handle all exceptions that happen on your service class and turn them into a FaultException or FaultException exception.
That way, you won't "fault" (or tear down) the communications channel between your client and server.
Even better approach would be to implement the IErrorHandler interface on your service class that provides a way to globally catch all exceptions as they happen and provide a FaultException instead, that's SOAP compliant.
You can even turn your IErrorHandler into a configurable behavior that can be turned on or off in config.
See these articles and blog posts for more details:
Rory Primrose: Implementing IErrorHandler
Useful WCF behaviors: IErrorHandler
Create a custom fault class that is marked with the DataContract attribute
Mark the method on the service contract interface with FaultContract. Ie. [FaultContract(typeof(CustomFault))]
In your service method, catch any applicable internal exceptions and throw a FaultException<CustomFault>. Alternatively, as marc_s mentioned, you can use IErrorHandler to map the exception to the fault.
Personally, I create a base Fault class that has a Reason property and I extend all custom faults from this class. When I want to throw the fault, I call:
throw Fault.Create<CustomFault>(new CustomFault("Boo hoo"));
It's also worth noting that I version my fault classes (including the common Fault class) along with all my other services. This is only a concern if service versioning is a concern, though.
Here's the basic Fault class (I've removed argument validation for brevity):
[DataContract(Namespace = XmlVersionNamespace.FaultNamespace)]
public abstract class Fault
{
internal FaultReason Reason { get; set; }
protected Fault(string reasonText)
{
Reason = new FaultReason(new FaultReasonText(reasonText, CultureInfo.CurrentUICulture));
}
public override string ToString()
{
return Reason.ToString();
}
internal static FaultException<TDetail> Create<TDetail>(TDetail fault) where TDetail : Fault
{
return new FaultException<TDetail>(fault, fault.Reason);
}
}
You can design the specific Fault Data Contracts for each of the exception scenario in your WCF service so that you can handle the fault/exception at client side respectively.
try
{
// Actions
}
catch (Exception ex)
{
// Log the exception
// Throw Fault Exception back to client
FaultException fe = new FaultException(ex.Message, new FaultCode("Your fault code"));
//throw fault exception back to WCF client
throw fe;
}
Related
I have a very simple 3-tier project. There is a DAL (data access layer) WCF service interacting with a data store; a BLL (business logic layer) WCF service as an intermediary between the DAL service and a client UI, and a Windows Forms client layer.
I declare a DataContract in my DAL service to act as a fault contract:
[DataContract]
public class ValidationFault
{
[DataMember]
public String Message { get; set; }
[DataMember]
public String PropertyName { get; set; }
...
}
My DAL service has an operation decorated with a FaultContract attribute:
[OperationContract]
[FaultContract(typeof(ValidationFault))]
Patient CreatePatient(Patient patient);
The implementation of CreatePatient throws a strongly-typed FaultException like this:
throw new FaultException<ValidationFault>(new ValidationFault("Patient last name cannot be empty.", "LastName"));
My BLL service acts as a client for the DAL service, and a service for the UI layer. In my BLL service I call the CreatePatient method of the DAL service, and if I catch the FaultException fault, I simply want to re-throw it to be handled by the client. The relevant BLL code looks like this:
...
catch (FaultException<ValidationFault>)
{
throw;
}
I can inspect the exception in the BLL and can confirm that it is a strongly-typed FaultException and the Detail section passed from the DAL intact. The BLL method attempting to rethrow the exception is decorated with the same [FaultContract] attribute as the DAL method above.
The UI client, which is a client of the BLL service, tries to handle the exception and log/display the appropriate information. Trouble is, when this fault reaches the client, it is no longer a strongly-typed FaultException, but instead a general FaultException with a null Detail section.
I did find that if instead of just rethrowing the fault in the BLL method, I instead recreate it with the same parameters like this:
catch (FaultException<ValidationFault> valEx)
{
throw new FaultException<ValidationFault>(new ValidationFault(valEx.Detail.Message, valEx.Detail.PropertyName));
}
then it reaches the client as a strongly-typed FaultException as expected.
My question is: why do I need to recreate this exception in the BLL service? Why can't I just pass through the strongly-typed FaultException thrown by the DAL service? I got the code to work but would like to understand what is going on. I imagine it's something that's staring me in the face but I can't for the life of me figure it out.
Indeed the answer was staring me in the face all along, so in case this helps someone down the road: my problem was the Action property of the FaultContract. When you decorate an operation with the FaultContract attribute, unless you specify Action explicitly, WCF automatically generates it for you. This Action includes the FaultException namespace, as well as the service and method names of the operation that generated the fault. In my case, the service and method name in my BLL layer were different than the service and method name of my DAL method that originally generated the exception, so although both my DAL and my BLL layer operations were decorated with an identical attribute:
[FaultContract(typeof(ValidationFault))]
... the Action of the FaultContract was different. Thus when trying to rethrow the DAL FaultException in the BLL service, the FaultContract did not match the one specified on the BLL operation, and WCF was repacking the exception as a general FaultException instead of a strongly-typed one.
My options in this case are to either re-create the FaultException in my BLL service, which creates the proper Action, but when it reaches my client, it appears to be coming from the BLL service; the fact that it originates in DAL is lost unless I specify it explicitly, or else add the Action property to the FaultContract attribute on the BLL operation, and specify the Action of the fault generated by the DAL. As to which is a better idea and better practice, that is likely a whole other topic, but I now have an understanding of what is going on, which is what I was after.
I have a workflow service. I also use workflow persistence in that service. But after I deployed workflow in IIS, from client I make a request to workflow service, in log file on server. I see a message
The execution of the InstancePersistenceCommand named {urn:schemas-microsoft-com:System.Activities.Persistence/command}SaveWorkflow was interrupted by an error.InnerException Message: Type 'System.ServiceModel.Channels.ReceivedFault' cannot be serialized.
Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute.
If the type is a collection, consider marking it with the CollectionDataContractAttribute. See the Microsoft .NET Framework documentation for other supported types.
I tried research about this exception, but I didn't find anything.
How to fix this problem ? or let me know what is the reason about above exception ?
System.ServiceModel.Channels.ReceivedFault is a non-serializable internal .NET framework class, so unfortunately there is nothing you can do to correct the actual root cause (i.e. making said class serializable).
You are probably calling an external service via WCF which faults, i.e. a System.ServiceModel.FaultException is thrown. IIRC, somewhere deep down in that FaultException object is a reference to the culprit, a ReceivedFault instance.
Solution: Catch all FaultExceptions, transfer the information you need to know from the FaultException into a serializable exception, and throw the latter:
try
{
CallService();
}
catch (FaultException ex)
{
// Gather all info you need from the FaultException
// and transport it in a serializable exception type.
// I'm just using Exception and the message as an example.
throw new Exception(ex.Message);
}
I know a wcf method throws fault exception and client application catches that fault exception. But is there any way to know that whether that wcf method throws fault exception or not at client side ?
When you create a WCF service you could decorate your operation contract with the [FaultContract] attribute:
[ServiceContract]
public interface IService1
{
[OperationContract]
[FaultContract(typeof(MyFaultContract))]
void DoWork();
}
This emits information about this MyFaultContract in the metadata (WSDL) of the service. Then when you create a client proxy (either using the Add Service Reference in VS or directly by the svcutil.exe) this fault contract is known by the client and you can catch exceptions of this type.
So the idea here is to look whether your operation contract is decorated with the [FaultContract] attribute to know which type of fault contracts this operation might throw. If it hasn't any custom FaultContract attributes defined on it it means that on the client you can catch only for the non-generic version of the FaultException.
I understand that WCF will convert an exception into a fault and send it back as a SOAP message, but I was wondering if this is truly interoperable. I guess I'm having a tough time trying to figure out this possible scenario:
Client (Java) calls a WCF Service
(LoginService).
Server checks for proper authorization, user authorization fails.
Server throws an UnauthorizedAccessException.
WCF converts this into a Fault somehow. (* - See Below As Well)
Client has to be able to know how to read this Fault.
I guess I'm just having a tough time understanding how this could still be interoperable because it is expecting Java to know how to translate a SOAP Fault that .NET encodes from an UnauthorizedAccessException.
Also, how does .NET actually convert the exception to a fault, what goes in as the fault code, name, etc. Some of the things seem to be "duh"s like perhaps the Fault Name is "UnauthorizedAccessException", but I'd rather know for sure than guess.
There is no "automatic conversion". WCF will return a fault (I forget which one) when you have an unhandled exception. But since you didn't declare that fault, many, if not most, clients will fail if you return it.
You are meant to define your own faults and to return them instead. Consider:
[DataContract]
public class MySpecialFault
{
public string MyMessage { get; set; }
}
[ServiceContract]
public interface IMyService
{
[FaultContract(typeof (MySpecialFault))]
[OperationContract]
void MyOperation();
}
public class MyService : IMyService
{
public void MyOperation()
{
try
{
// Do something interesting
}
catch (SomeExpectedException ex)
{
throw new FaultException<MySpecialFault>(
new MySpecialFault {MyMessage = String.Format("Sorry, but {0}", ex.Message)});
}
}
}
Any client capable of handling faults will deal with this. The WSDL will define the fault, and they will see a fault with the Detail element containing a serialized version of the MySpecialFault instance that was sent. They'll be able to read all the properties of that instance.
Faults have been part of the SOAP specification since v1.1. They are explained in the SOAP Specification.
It is up to implementations (WCF, Java etc) to ensure that Faults are handled according to the specification.
Since WCF converts FaultExceptions to Faults according to the SOAP specification, FaultExceptions thrown from WCF are interoperable.
SOAP faults are interoperable but .Net exception classes are not good to be used in SOAP faults. Instead define your own DataContract class (e.g. AccessFault) and then use it in a FaultContract.
see http://msdn.microsoft.com/en-us/library/ms733841.aspx
Whenever there is a UnauthorizedAccessException thrown at service boundary convert it to FaultException.
This can be done in several ways like using Microsoft Enterprise Library Exception Handling Block or implementing the IErrorHandler interface.
When a FaultException is returned from my WCF service, I need to Abort the channel instead of Closing it. Both my client and service work fine with this approach but after implementing IErrorHandler on the service and logging any exception, I can see that calling Abort on the client causes the service to log:
System.ServiceModel.CommunicationException: The socket connection was aborted...
I do not want to pollute my service logs with this information and only want to log service related errors. I know I can obviously stop logging any CommunicationExceptions but my service is also a WCF client for other services and CommunicationExceptions raised by these services should be logged.
How can I stop it doing this?
As nobody else has answered the question (Tomas's answer was not relevant), I asked a few experts in the field. Unfortunately, there is no nice way of stopping this and the best that they could come up with was to add logic in IErrorHandler to not log CommunicationExcepions with a message starting with 'The socket connection was aborted'. Not very elegant, but it does work.
The problem is that you get an exception that covers your underlying exception if you get an exception when calling dispose wich is possible. I wrote a wrapper to deal with scenarios like this, you can read about it on my blog: http://blog.tomasjansson.com/2010/12/disposible-wcf-client-wrapper/
The idea is that you have a wrapper around your channel that deals with the scenario if the dispose method throws an exception.
A small example of how you should use my wrapper:
public class ClientWrapperUsage : IYourInternalInterface
{
public IList<SomeEntity> GetEntitiesForUser(int userId)
{
using(var clientWrapper = new ServiceClientWrapper<ServiceType>())
{
var response = clientWrapper.Channel.GetEntitiesForUser();
var entities = response.YourListOfEntities.TranslateToInternal();
return entities;
}
}
}
Here I have assumed that it existing an extension method for a list that contains the entity that is returned by the service, then you use that method to translate it to internal entities. This is 100 % testable, at least I think :). Just moch the interface IYourInternalInterface everywhere you wan't to fake the service.