In my previous previous post on the WCF Fault Spike and the other post on 4 Tenets of Service Oriented Data Validation I discussed some options and best practices for service data validation with WCF. In this post I want to consider how the same scenario applies to WF4.
Download Sample Code – WCF Service Fault and Validation Example
I created an application that ran three scenarios each resulting in an exception thrown from the service and a web UI page then shows what the sender can do based on the fault scenario. Here are my WF results for the same scenarios.
"The server was unable to process the request due to an internal error. For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the <serviceDebug> configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework 3.0 SDK documentation and inspect the server trace logs."
No surprises here, probably anyone who has done Workflow Services for more than 5 minutes has run into this. For more information see the WCF documentation Sending and Receiving Faults. You might be tempted to just turn on IncludeExceptionDetailInFaults but don’t do it because it can lead to security vulnerabilities. Instead you need a better strategy for dealing with exceptions and that means you need to understand FaultException.
As we saw in the previous example, WCF already throws a FaultException for you when it encounters an unhandled exception thrown from a Workflow Service. The problem is in this case that we want to let the caller know they sent an invalid argument even when they are not debugging.
public sealed class ThrowSenderFault : CodeActivity
{
public InArgument<string> Reason { get; set; }
protected override void Execute(CodeActivityContext context)
var faultReason = context.GetValue(this.Reason);
// Let the sender know it is their problem
// Workaround for creating a version aware FaultCode
var faultCode =
FaultCodeFactory.CreateVersionAwareSenderFaultCode(
ContractFaultCodes.InvalidArgument.ToString(), Service.Namespace);
throw new FaultException(faultReason, faultCode);
}
The main thing is that the client now gets a message saying that things didn’t work and its their fault. They can tell by looking at the FaultException.Code.IsSenderFault property. In my code you’ll notice a class I created called CreateVersionAwareSenderFaultCode to help deal with the differences between SOAP 1.1 and SOAP 1.2. You will find it in the sample.
Some interesting things I learned while testing this
While this is better than throwing unhandled exceptions, there is an even better way and that is FaultContracts
This is the best choice. It allows you to pass all kinds of information to clients and it makes your error handling capability truly first class. In the sample code one thing I wanted to do was to use the FaultException.HelpLink property to pass a Url to a help page. Unfortunately I learned that none of System.Exception’s properties are propagated to the sender. No problem, I just added a HelpLink property to my ArgumentValidationFault type and used it instead in FaultException.Details.HelpLink
You should use Fault Contracts with Workflow Services, but there are two issues to consider.
In Workflow Services, contracts are inferred (including fault contracts) to infer a Fault Contract
If you want to do validation within your workflow you can use an “If” activity (for example) and if the condition is valid., send back the normal result and if the condition is invalid use the SendReply that sends a FaultException.
If you have declared a fault contract (as shown above) then the Fault Detail Type will be emitted into the WSDL so any code you like can throw the FaultException<TDetail>, If you did not declare a fault contract then your code can throw a FaultException<TDetail> but the contract inference will not be aware of this and will not emit the fault contract. Senders will be able to catch FaultException but they will not have the fault detail type in their service reference and will not be able to catch FaultException<TDetail>
Using the Throw activity does not change the situation at all. You still have to declare the fault contract even if you never invoke the SendReply in order to emit the fault detail type into the WSDL.
Ron http://blogs.msdn.com/rjacobs Twitter: @ronljacobs
Ron, I have watched this screencast several times and definitely have a use for validating my data. I see you using manual validation on properties... Is there anyway to validate data using the Entity Framework?
You would have to be able to get EntityFramework to throw a FaultException<TDetail> when the validation fails. I don't think there is a way to do this.
However, you could wrap the EntityFramework object inside of another type (similar to the way I did GetDataRequest) and then put the value inside of a Try/Catch block. Catch the internal exception from EF and throw a FaultException from there.
Ahhh this is good...
I send a PDF from the client (SL) UI, so no real validation on client. Then on the server (via WCF) open it up and parse out the fields then validate and put in a DB if ok. I actually would like to validate using the EF object and then get all the invalid properties at that time and throw using the FaultException(TDetail>. Since I would like to send all invalid property details back to the user this would be better.
Ron, I have gotten this to work without a hitch with content, but I am uncertain how I could accomplish this with with parameters content? The reason is because I want to make my workflow service act just like a wcf service as you discussed here: blogs.msdn.com/.../making-a-workflowservice-work-like-a-wcf-service.aspx. Do you have any ideas. I also wanted to say thanks for the videos, they are a major help.
If you send simple types as parameters then of course this approach won't work. Why not send the type with property setter as a parameter? Or do you have to implement an existing contract?