locked
WCF: Error Code vs Exception? RRS feed

  • Question

  • Hi Guys,

     

    We are writing WCF services which are utilizing COM+ application, now we are unable to decide on exception management i.e. whether to use error codes or exception for business errors. Let me give you an example:

     

    public Document GetDocument(int docId)

     

    Now there can be following outputs when you call this method

     

    - throws exception if database is down

                Can be easily handled via exception management

     

    - Returns null if document not found     

    - Returns null if document is not in active state (Business case)

     

    Now the above cases, they return the same output but the reasons are different and we want to show the reason to the client, what can be the best practice?

     

    One of the options we were thinking of

     

    public Document GetDocument(int docId, out enum returnErrorCode)

     

    so enum can have values DocumentNotFound, DocumentNotInActiveState etc

     

    First of all we don’t like this approach and secondly caller will have to do lot of if checks after the method call which is weird.

     

    The other option we are thinking to have two type of base exceptions derived from WCF FaultException, one to handle all unknown/technical exceptions and one to handle pure business errors with ErrorCode in it. The only benefit we'll be getting with this approach that WCF clients will be handling just these exceptions instead of writing various if checks. But still not sure it’s correct to handle business errors via exceptions.

     

    Please advice.

     

    Regards

    Faraz

    Thursday, July 5, 2007 9:58 AM

Answers

  • My blog post that was quoted earlier is here.
    Keep in mind that the goals are still the same whether or not you are involving services The goals are:

    1. Easy to understand, code and use
    2. Consistent with the intent of the language/technologydesigners and common practice
    With that said, I believe that exceptions are still the best way to go in either case. Some people have noted that exceptions may introduce a performance problem, however this is only in rare cases where the number of exceptions thrown are really excessive (like hundreds of thousands). If that is your concern, I would test the performance to know for sure.
    Wednesday, July 11, 2007 8:59 PM

All replies

  • For good service design I would say you should utilzie SOAP faults (in WCF the abstractions for SOAP faults are Fault Contracts and FalutException)
    Thursday, July 5, 2007 10:42 AM
  • First of all if you are developing using a service orientated paradigm why are you passing simple types to an end point surely you want to be submitting a 'request' message and receiving a 'response' message back, also naming a method 'GetXXX' seems like a bad idea to me.

     

    e.g.

     

    RetrieveDocumentResponse RetrieveDocument(RetrieveDocumentRequest)

     

    The advantage of this is that the response message 'RetrieveDocumentResponse' can contain a status field indicating success, the document and a error message field etc...

     

    Personally I prefer not to use exceptions to convey errors in business logic across a service boundary, I prefer the above approach. I only expect exceptions from infrastructure\message (versioning)\hosting problems, e.g. the end point of the web service is not available (404 error) etc

     

    Hope this helps

     

    Ollie Riches

    Thursday, July 5, 2007 2:16 PM
  • Well agree with you but based on circumstances you cannot follow the full practice some time, can you? So it's a kind of SOA but not 100% of course, but anyway the question remains same Error Code or Exception?

    Regards
    Faraz
    Friday, July 6, 2007 8:20 AM
  • You raise the more generic question of how to handle multiple (and multiple kinds of) responses - one of WSDL's shortcomings.

     

    Consider moving to a generic signature like: IMessage[ ] Execute(IMessage[ ] input);

     

    Then consider moving to a publish/subscribe model.

     

    Finally, you'll find that (as this post shows) you don't need any request/response semantics for getting information .

     

    Give it a try. It's more scalable than the synchronous request/response the tools auto-magically spit out.

    Sunday, July 8, 2007 7:45 PM
  • You could try a more simplistic approach:

     

    GetDocumentResponse GetDocument(GetDocumentRequest request)

     

    GetDocumentRequest would have everything you need to call the service (input parameters, service/application parameters, etc.)

     

    GetDocumentResponse would have the data you want to retrieve and the execution status.

     

    You should leave SOAP as transport only. That way you can easily consume/expose services using other "protocols" (like REST, RSS, etc.).

     

    This suggestion doesn't invalidate anything Udi said about the publisher/subscribe model. It's just a simplier and not so generic service contract.

    Monday, July 9, 2007 10:34 AM
  • re: exception vs error code
    The argument in services is, IMO, exactly the same as it is anywhere else. If you don't expect the problem to ever happen then it is an exception. If you are breaking a business rule then you are probably expecting it to be broken and therefore it isn't an exception.

    Monday, July 9, 2007 9:28 PM
  • My point is that SOAP is transport and SOAP faults should be only for transport errors.

     

    Some error might not be a business error from the producer's standpoint, it certainly is one form the consumer's standpoint.

     

    You don't expect to receive a FileNotFoundException because the products file is missing when you call GetProducts. Instead you should get some sort of internal error.

    Monday, July 9, 2007 10:50 PM
  • I'd have to agree with that, you wouldn't expect an infrastructure file to be missing that would be ideal for an exception. However I'm still unclear as to what this thread is suggesting as the answer. The quote from Ron Jacobs is interesting...

    According to the .NET FrameworkClass Library Design Guidelines

    “Exceptions are the standardmechanism for reporting errors. Applications and libraries should not usereturn codes to communicate errors. The use of exceptions adds to a consistentframework design and allows error reporting from members, such as constructors,that cannot have a return type. Exceptions also allow programs to handle theerror or terminate as appropriate. The default behavior is to terminate anapplication if it does not handle a thrown exception.”

     

    Consider a seemingly simply business rule, "Can you transfer your x units from account 1 to account 2?". The rules check for; a) account 1 exists b) have rights to account 1 c) does account 2 exists...etc, n) does account 1 have the funds n+1) is account 2 open?

     

    As a service your "contract" is explain exactly the rule that was broken. If we were to recommend the error code result it would require a large switch statement to process all the "error" codes - not nice and difficult to maintain. If we were to recomment an exception per rule we'd end up with a horrible set of catch (n)...catch(n+1) structure, IMO worse still (at least a switch can group codes). I believe what we're suggesting is that you'd have one TransferException with arguments describing the specific results? But wouldn't that simply result in one catch(n) and a switch statement on the argument? I suppose at least this solves the "you can ignore an error code" and the horrible catch(n)...catch(n+1) problems.

    Or have I missed something?

     

     

    Wednesday, July 11, 2007 8:13 AM
  • I think service invocations should be treated as low level invocations and exceptions should be a high level mechanism.

     

    A SOAP fault should be a transport error when using SOAP. When using REST, HTTP status codes and messages should be used.

     

    When using SOAP faults you can get a FaultException or a SoapException depending whether you are using WCF or ASMX. Should you build your service consumers be aware of the API over the transport either?

     

    And you can also be accessing a database or a file (on a local drive or via FTP).

     

    You should shield all this from the business invocation code and throw transport or communication exceptions.

     

    As for business rules, a business exception should be thrown.

     

    With this pattern, everyone will be happy:

    ·         No FaultExceptions or SoapException

    ·         Exceptions thrown when an error occurs.

    Wednesday, July 11, 2007 8:44 AM
  • IMO fault\error\exception (which ever you like to call it) is never nice to look at in the 'real' world of application development, whether it be a exception handling strategy like the enterprise library exception handling block, detailed error messages\codes or multiple 'catch' statements.

     

    Just as long as you decide on a strategy you can justify and use it every where then it should be okay.

     

    As I & others have stated above I believe you shouldn't throw exceptions from a service - detailed error codes\messages should be returned in the response message, only the communication infrastructure (e.g. SOAP via HTTP) should throw exceptions.

     

    HTH

     

    Ollie Riches

    Wednesday, July 11, 2007 8:49 AM
  • Sorry guys for coming so late on it, anyway i think you guys are thinking totally from WCF perspective and whatever comments you all have given are totally correct in WCF world. Ok lets say if i remove SOA all together and you have COM+ services in place of it, what you will do than? or if i keep service layer either as WCF or COM+ or webservices but there's a business layer written in custom .net classes what they'll return then?
    like lets say WCF ProductManagerService calls CreateProduct and internally it calls Create of Product BLL class then how this BLL would handle the errors? via exception or error codes?

    Regards
    Faraz
    Wednesday, July 11, 2007 3:05 PM
  • My blog post that was quoted earlier is here.
    Keep in mind that the goals are still the same whether or not you are involving services The goals are:

    1. Easy to understand, code and use
    2. Consistent with the intent of the language/technologydesigners and common practice
    With that said, I believe that exceptions are still the best way to go in either case. Some people have noted that exceptions may introduce a performance problem, however this is only in rare cases where the number of exceptions thrown are really excessive (like hundreds of thousands). If that is your concern, I would test the performance to know for sure.
    Wednesday, July 11, 2007 8:59 PM
  • I have never done any real work with COM+ but I've done some mixed REST/ASMX/WCF clients and I thinl I would do the same for COM+ - an abstraction layer for comunication and business errors would be on the message (or data transfer object).

     

    As I said before, that doesn't prevent you from throwing a business exception from the client service calling code to the code calling the code that calls the service.

    Wednesday, July 11, 2007 9:11 PM
  • Let me just code what I mean.

     

    This would be the service consuming code:

     

    Code Snippet

    // ...

     

    IProductsService productsService = ServiceContainer.GetService();

     

    try

    {

      Product[] productsService.GetProducts(category);

    }

    catch (InvalidProductCategoryException bex)

    {

      // ...

    }

    catch (ProductSystemException exe)

    {

      // ...

    }

     

    // ...

     

     

     

    This would be a service adapter for WCF:

     

    Code Snippet

    public class WcfProductsServiceAdapter : IProductsService

    {

      public Product[] GetProducts(string category)

      {

        GetProductsRequestMessage request Translators.Get(category);

     

        Adapters.BeforeSendMessage(request);

     

        GetProductsResponseMessage response;

        using (ProductsServiceClient client new ProductsServiceClient())

        {

          try

          {

            response = client.GetProducts(request);

          }

          catch(Exception ex)

          {

            throw new ProductSystemException(ex.Message, ex);

          }

        }

     

        Adapters.AfterReceiveMessage(response); // throws business exceptions

                                                // based on error information

                                                // on the respone message.

     

        return Translators.GetList<Product>(response);

      }

    }

     

    Using this pattern, you can have your code receiving business exceptions even if the comminication code is nto using exceptions for business errors.

     

    Using this pattern, you can implement your service adapter using WCF with MessageVersion.None, ASMX, COM+, database, XML file, etc.

     

    I hope I understood correctly the question: WCF, FaultException and business errors, right? 

     

    Wednesday, July 11, 2007 9:36 PM
  • re: the performance argument.
    I concede that normally you'd put maintenance ahead of squeezing out performance but I'm not so sure it's as small as you suggest, especially in a distributed world. If that exception has to be serialized back a few hops then it is going to start looking slow.  Given that we should ideally be designing code that works on a PDA and a 1000 unit farm it is difficult to implement an exception to the exception rule! But how many people really design like that I wonder?

    On this note I'm sure I read that Microsoft were looking to change how exception handling worked and it promised to alleviate some of the stack walking, did I dream this or does anyone else know about it?

    Wednesday, July 11, 2007 9:48 PM
  • re: Paulo Morgado
    But why spit the two out? Obviously the general catch (Exception) is for the example but why wouldn't you catch transport exceptions along with business exceptions. I don't really understand what you're gaining from the approach (my fault I'm sure). If you wanted to be clear couldn't you nest it like

    try
        Call Biz Component
           try
               Make WCF call
           catch(transportException1)
           catch(transportExeption2)
    catch (BizException1)
    ...
    where the nested try would be another function?

          



    Wednesday, July 11, 2007 9:58 PM
  • Me example wasn't that simple. The code using a particular IProductsService isn't aware of any particular implementation nor transport. So, it would be very wrong to catch a FaultException if you are retrieving products from an XML file.

     

    Maybe I've been CABing too much latelly.

    Wednesday, July 11, 2007 10:11 PM
  •  Faraz_Ahmed wrote:

     

    public Document GetDocument(int docId)

     

     

    One major problem with writing RPC style methods like this is that you have no way of returning out-of-band information.  In this particular example, you are asking about how to propogate error codes and other information from the service to the consumer.  Using a design like this, you must use transport-level mechanisms (soap faults, etc) to propagate out-of-band information.

     

    What several people are trying to point out is that by bringing your abstraction up a level, you gain the ability to convey the out-of-band data without using some behind-the-scenes magic (ie..soap faults).

     

    Generally, this is done by moving away from RPC methods (as seen above) into a more messaging friendly api, such as this one:

     

    public GetDocumentResponse GetDocument(GetDocumentRequest request);

     

    Now you can encapsulate the details of the request into a single "object".

     

    Code Snippet

    public class GetDocumentRequest

    {

       private string _docId;

     

       public string DocId

       {

          get { return _docId; }

          set { _docId = value; }

       }

    }

     

    We also do the same with the response:

     

    Code Snippet

    public class GetDocumentResponse

    {

       private Document _document;

     

       public Document Document

       {

          get { return _document; }

          set { _document = value; }

       }

    }

     

    Now that our request and response are contained inside a request/response pair, we can use a higher layer of abstraction to solve our out-of-band data problem: 

    Code Snippet

    public abstract class Request

    {

       private string _requestId;

     

       public string RequestId

       {

          get { return _requestId; }

          set { _requestId = value; }

       }

    }

     

    Code Snippet

    public abstract class Response<T>

    {

       private string _correlationId;

       private T _request;

       private bool _success;

       private string[] _errors;

     

       public string CorrelationId

       {

          get { return _correlationId; }

          set { _correlationId = value; }

       }

     

       public T Request

       {

          get { return _request; }

          set { _request = value; }

       }

     

       public bool Success

       {

          get { return _success; }

          set { _success = value; }

       }

     

       public string[] Errors

       {

          get { return _errors; }

          set { _errors = value; }

       }

    }

     

     The above shows a very simple implementation of the Correlation Pattern (for correlating requests and responses on the consumer side) as well as the ability to pass out-of-band data to our consumer.

     

    After defining our base types, we can simply use them with our messages:

     

    public class GetDocumentRequest : Request

    public class GetDocumentResponse : Response<GetDocumentRequest>

     

    By using the RPC style method and returning the entity directly, you back yourself into a corner.  You will need the flexibility down the road (if not now).

     

    I would also recommend not using a string[] for the errors, introduce another object just to convey error information (ie.. something that has an Id and Message property). 

     

    If you ever have to support multilingual exception handling across a service boundary, you will understand why (trust me, I'm there now).

     

    And if you are looking for some sample code, you can check out my blog:

    http://evanhoff.com/archive/2007/06/28/22.aspx

    http://evanhoff.com/archive/2007/07/01/23.aspx

     

    If you can follow me this far, I show you in my blog how to bundle multiple "Requests", send them across the wire, unbundle them, process them, bundlle the "responses", and then send them back to the consumer.  All while using only a single network traversal.  You could never do that with an RPC style interface.

     

     Also, take a look at the Notification Pattern.  It makes the exception handling (at least for checking preconditions) much simpler to write.  You place it into some goo between the metal (WCF/ASMX/etc) and your application logic.

     

    Thursday, July 12, 2007 1:03 AM
  • Re-reading your example I think we're almost saying the same thing, except I'm talking about something like...

     

    You have...

    Code Snippet

    Adapters.AfterReceiveMessage(response);

     I'd suggest

    Code Snippet

    Adapters.AfterReceiveMessage(exception);

     

     where exception is one that you haven't specifically caught in the call. So both methods hide the specific tranport but one requires the error information inside the response the other continues with the exception paradigm.

     

     

     

    Thursday, July 12, 2007 12:13 PM
  • re: Evan H

    What concerns me with that approach is it seems that you're changing your implementation to fit the technology rather than representing the actual problem. In the example the client wants a Document from a service not a DocumentResponse. Perhaps you've just skipped over something and I've missed the point?

     

    Thursday, July 12, 2007 12:20 PM
  • This:

     

    Code Snippet

          catch(Exception ex)

          {

            throw new ProductSystemException(ex.Message, ex);

          }

     

    Should be replaced for something like this:

     

    Code Snippet

          catch(Exception ex)

          {

            Adapter.ExceptionPolicy(ex); // Throws the apropriate exception.

          }

     

    But that would still only handle transport/communication exceptions.

     

    Business errors should still be part of the message (as Evan illustrated) and converted to the appropriate message.

     

    These adapters are very usefull in filling parts of the request message with things like client credintials, collection session information, etc.

    Thursday, July 12, 2007 1:00 PM
  • It doesn't change the logical implementation. It just hads another layer (you know, like changing binary data into XML, changing XML into binary data, changing binary data into moving electrons/photons, etc.).

     

    With WCF you can do most of those thing outside the service or client and change them by configuration (see how validation is applied to WCF contracts using the Validation Application Block in the Enterprise Library 3.1).

    Thursday, July 12, 2007 1:05 PM
  • But the point is that you're writing code that seems to need to understand that this conversion is taking place. The client wants a Document, what happens inside the black box of the API/Service should be irrelvant to the client. WCF contracts\policies and the like are all there to help configure transport so the client doesn't have to know (as you suggest in your post). That's the bit I'm missing from your example, where do they get Document and not DocumentRequest? I'm sure we're all probably saying the same thing I just can't see it at the moment.

     

     

    Thursday, July 12, 2007 3:30 PM
  •  pkr2000 wrote:

    re: Evan H

    What concerns me with that approach is it seems that you're changing your implementation to fit the technology rather than representing the actual problem. In the example the client wants a Document from a service not a DocumentResponse. Perhaps you've just skipped over something and I've missed the point?

     

     

    Yes, it's true that the client wants the document.  However, it's also true that the client actually wants more than the document.  They also want to be notified if something goes wrong.  As Paulo said, the Request/Response is simply an extra layer of abstraction around the data. 

     

    Your ethernet frames have out of band data (ie..a MAC address).  They encapsulate your TCP packets.  Your TCP packets have header information (IP addresses and such).  TCP enapsulates HTTP.  HTTP includes header data (http headers, status codes, etc).  In this particular case, HTTP is encapsulating SOAP.  SOAP also has header data.  SOAP encapsulates our request.  In the example above, we are encapsulating application data (the document) inside an application protocol (a request/response pair).

     

    You can use SOAP faults to pass exception handling data to your consumer.  You could also use HTTP status codes (I would bet most REST implementations do).  You could also poke your exceptions into the TCP header.  I think nearly everyone would agree that putting your exceptions inside the TCP header is silly.  It's not even practical from a development perspective.  But if it was "really easy" would you?  Why not?

     

    I won't argue that using SOAP faults is bad, however, I will argue that adding that extra layer in the stack (the request/response) gives you a lot of room to work with.  Be thankful all the layers below you did, otherwise your SOAP faults would be propagating all the way into the TCP header (or lower) anyway.

     

    Ask yourself a question.  How will I handle scenarios where a particular call needs to return 2 different entities (of different types)?  Using a request/response pair, this is trivial.  We just add another object to our Response object.  For an example scenario, think of an application which provides document storage as a service (for a fee).  You might want to verify the customer has enough bandwidth in their account to service the request (before sending the Document).  You might also want to send back an AccountUsageStatus object along with the Document.  Using a GetDocumentResponse, no problem.  If you are sending a Document back directly, either you can't or you have to modify your Document to include the data (ouch).

     

    Error handling is another good example.  With the Request/Response, we simply leave a place to put error messages.  In the RPC style, we have to fall back on the layer below us (soap faults).

     

    There's also a strong argument that the example above is more portable than relying on SOAP faults.  The example above is ignorant of the layer directly below it.  This means we could swap it at will to meet the needs of our consumers.  It may not be useful right away, but its nice to have that cushion if we need it down the road (and for very very little extra work).

     

    So I guess when you break it all down, in answer to your question, I would say that to facilitate application level exceptions, we should use an application level abstraction.  Lots of people use SOAP faults, so I won't say that's "bad".  However, bringing yourself up a level will give you much much more beyond answering the exception handling question.

     

    It also promotes a less "chatty" style of api, which is a very good thing considering that traversing the network is a very expensive thing to do.

    Thursday, July 12, 2007 3:30 PM
  •  pkr2000 wrote:

    But the point is that you're writing code that seems to need to understand that this conversion is taking place. The client wants a Document, what happens inside the black box of the API/Service should be irrelvant to the client. WCF contracts\policies and the like are all there to help configure transport so the client doesn't have to know (as you suggest in your post). That's the bit I'm missing from your example, where do they get Document and not DocumentRequest? I'm sure we're all probably saying the same thing I just can't see it at the moment.

     

     

     

    We need to not forget the 8 fallacies of distributed computing:

    http://michael.toren.net/mirrors/eight-fallacies-of-distributed-computing/

     

    One problem with building distributed apis which make remote calls transparent is that they quickly get abused by us developers (and client developers).  The fact that the client is making a call across a network should be clear to the client.  To make the call transparent is to invite people to forget that network calls are expensive, not to mention all the things that can go bad between making the call, serializing the information, crossing the network, processing, serializing the data, crossing the network again, and deserializing the data.

     

    Objects which handle processing locally and objects which handle processing remotely are vastly different.  They should be treated as such.

    Thursday, July 12, 2007 3:48 PM
  • Although we are sharing a number of points I actually I think we are not on the same train of thought at all.

    1. I absolutley agree that there should be levels of abstraction

    2. Transports exceptions should be hidden from the biz' consumer

    3. Chatty interfaces are bad

     

    However...

    I firmly believe that you should always strive to design your components to be scale agnostic i.e. it should work on a PDA and it should scale on million machine web farm. Now obviously part of that is not to design a component that has a chatty API. I also believe that the fundamental advantage of technologies such as WCF is that you can change the transport without changing the client. Therefore the client should not need to know about the transport and therefore shouldn't need to be implemented differently. So if you feel that a RetrieveMyFile API would have a number of possible "out" objects then fine design it that way. But I really dislike the idea of what seems (maybe unfairly) to be a type-unsafe name/pair list and I especially don't like the way it advertises how it has been implemented. I truly believe you can successfully design a component that looks like a straightforward component from a UML diagram without having to expose any clue to it's underlying implementation and still provide a component that performs and scales well. Perhaps I'm just a dreamer, perhaps I'll just have to agree to disagree.

     

     

     

    Thursday, July 12, 2007 4:08 PM
  •  pkr2000 wrote:

    I firmly believe that you should always strive to design your components to be scale agnostic i.e. it should work on a PDA and it should scale on million machine web farm. Now obviously part of that is not to design a component that has a chatty API. I also believe that the fundamental advantage of technologies such as WCF is that you can change the transport without changing the client. Therefore the client should not need to know about the transport and therefore shouldn't need to be implemented differently. So if you feel that a RetrieveMyFile API would have a number of possible "out" objects then fine design it that way. But I really dislike the idea of what seems (maybe unfairly) to be a type-unsafe name/pair list and I especially don't like the way it advertises how it has been implemented. I truly believe you can successfully design a component that looks like a straightforward component from a UML diagram without having to expose any clue to it's underlying implementation and still provide a component that performs and scales well. Perhaps I'm just a dreamer, perhaps I'll just have to agree to disagree. 

     

     

    What makes you say "type unsafe"?

     

    As an interesting side note, this discussion is not new with WCF.  It's a recurring theme.

    http://www.ddj.com/dept/architect/184414966

    Thursday, July 12, 2007 4:51 PM
  • Interesting link and I agree that the idea of distributing your application to magically gain scalability is a myth. What I'm suggesting is as a vendor of a service component it is my job to ensure that it performs to whatever agreement/contracts (SLA) are in place. How I do that is up to me. I shouldn't place "odd" looking requirements on my client but neither should I present an API that can be abused. In the file storage example, if someone asks for a 100Terrabyte file and my spec say <10Mb then I'd raise an exception. If my spec says 100Tb file then I'd better ensure it does that efficiently. The underlying transport should not only be abstracted away but should, IMO, be hidden. A subtle but important difference. Just like I don't want to supply or recieve SQL Server components to/from my GetDocument API I don't want anything that suggests the transport. Now if you're working in an enclosed devleopment environment then there isn't any real advantage to what I'm advocating but I'd like to think that if something is done in this way then the concept propagates and a future external use will just be a happy side affect.

     

    Friday, July 13, 2007 9:28 AM
  • I think one of my team members said it best this week.  When you have a room full of designers and architects, when any two agree, you have a majority. lol

     

    I will agree that messaging looks "odd".  It looked odd to me the first time I saw it.  I think it looks natural to me now only because I've been working with it a lot lately.

     

    Rather than dig into it deeper, I think I'll just leave you with a few things so that if you are interested in checking out more of this "odd-looking stuff", it's within easy reach.  I did a quick writeup on my blog.

     

    Getting Your Feet Wet with Messaging

     

    There were some really talented people who posted in this thread, so maybe we can get one or two of them to drop their favorite links also.  I know I still have a lot to learn. ;-)

    Saturday, July 14, 2007 5:00 PM
  • Hi guys,

     

       about when and for what of throwing exceptions adequatedly, I always recommend Krzysztof Cwalina's talk on "Designing .NET Class Libraries"

     

       It's a session of about 4 hours (relax, continue reading ) on designing frameworks by examples taken from how the .NET class library was designed and implemented

     

       If you click on that session and go directly to the 2:23 (two hours, twenty three minutes) in the timeline, Krysztof starts talking about the good, bad and ugly of exceptions and how to use them wisely. Worth reading as that debate trascends the mere discussion of whether is good or not throwing exceptions from WCF services

     

       Cwalina was the architect of the .NET base class library and he wrote a book on designing frameworks

    Wednesday, July 18, 2007 7:52 PM
  • One option that hasn't yet been discussed is the use of a callback contract in WCF to create a duplex channel, rather than trying to put everything into a response document.

     

    Consider this:

     

    Code Snippet
    [ServiceContract(CallbackContract=typeof(IPayrollClient))]

    public interface IPayrollService

    {

    [OperationContract(IsOneWay = true)]

    void GiveSalaryIncrease(Guid employeeId, float amount);

    }

     

    [ServiceContract]

    public interface IPayrollClient

    {

    [OperationContract(IsOneWay = true)]

    void EmployeeNotFound(Guid employeeId);

     

    [OperationContract(IsOneWay = true)]

    void SalaryIncreasePendingAuthorization(Guid employeeId);

     

    [OperationContract(IsOneWay = true)]

    void SalaryIncreaseAuthorized(Guid employeeId);

     

    [OperationContract(IsOneWay = true)]

    void SalaryIncreaseNotAuthorized(Guid employeeId);

    }

     

     

     

    Personally, I don't like this RPC style but I think it's an improvement over the synchronous Request/Response examples shown before. It also supports the long-running workflow aspects of the service and enables multiple responses to be returned as the state machine progresses.

     

    Tuesday, July 31, 2007 7:48 AM
  •  Ron Jacobs wrote:

    My blog post that was quoted earlier is here.
    Keep in mind that the goals are still the same whether or not you are involving services The goals are:

    1. Easy to understand, code and use
    2. Consistent with the intent of the language/technologydesigners and common practice

    With that said, I believe that exceptions are still the best way to go in either case. Some people have noted that exceptions may introduce a performance problem, however this is only in rare cases where the number of exceptions thrown are really excessive (like hundreds of thousands). If that is your concern, I would test the performance to know for sure.

     

    I really agree on Ron's view on this. On the performance side, just avoid using exceptions to control business logic and it will work great. This was already pointed out by Evan on Ron's blog post.

     

    pkr2000, the reasons to use request/response approach suggested by Evan are beyond exception handling. Altough an overhead on some project, I like your view on designing the component to be scale agnostic. Some of the concerns to large scale components, that are to be used by third parties are usefull to a good design of the component's. Following this line of tough, I suggest the request/response approach, since it is really usefull enabling future extensions on the components while at the same time promotes not chatty interfaces.

     

    Evan, I don't agree on using the request/response approach for exception handling. As Ron commented in his blog, there are mecanisms to map exceptions to soap faults. Soap faults are part of the Web Service standard, and as such should be used to communicate exceptional situations (same reasoning why to use exceptions). On the client side, a mapping is also applied enabling an exceptions approach. My comments apply to exceptional situations, since I do think that normal situations that are part of the business logic should be part of the messages.

     

    I think that this last post by Evan H reveals an interesting approach. Below some further comments on the approach, how it relates to the request/response approach and exceptions.

     

     Udi Dahan The Software Simplist wrote:

    One option that hasn't yet been discussed is the use of a callback contract in WCF to create a duplex channel, rather than trying to put everything into a response document.

     

    Consider this:

     

    Code Snippet
    [ServiceContract(CallbackContract=typeof(IPayrollClient))]

    public interface IPayrollService

    {

    [OperationContract(IsOneWay = true)]

    void GiveSalaryIncrease(Guid employeeId, float amount);

    }

     

    [ServiceContract]

    public interface IPayrollClient

    {

    [OperationContract(IsOneWay = true)]

    void EmployeeNotFound(Guid employeeId);

     

    [OperationContract(IsOneWay = true)]

    void SalaryIncreasePendingAuthorization(Guid employeeId);

     

    [OperationContract(IsOneWay = true)]

    void SalaryIncreaseAuthorized(Guid employeeId);

     

    [OperationContract(IsOneWay = true)]

    void SalaryIncreaseNotAuthorized(Guid employeeId);

    }

     

     

     

    Personally, I don't like this RPC style but I think it's an improvement over the synchronous Request/Response examples shown before. It also supports the long-running workflow aspects of the service and enables multiple responses to be returned as the state machine progresses.

     

     

     

    The approach enabled by wcf is really a new level in terms of flexibility. I would use it only to communicate normal situations that are part of the business logic (as stated in my previous comments). The approach doesn't means not using request/response approach. On your example I would use a SalaryIncreaseRequest as the only parameter to GiveSalaryIncrease's, and one or more response classes to be the parameter of the IPayrollClient methods.

     

    I consider EmployeeNotFound an exceptional situation, thus I would not use it on the interface. The approach does raises some concerns on the design of the interfaces, since it can easily be wronly used to create a chatty design (meaning many bad things). This isn't a concern if one uses it to only server different responses a given request might have. The problem appears where the responses are reporting progress on a process, and the receiving application might use those intermediate responses to activate other process.

     

    Freddy Rios - www.gcm.com.ve/en

    Thursday, August 2, 2007 1:54 AM
  •  Freddy Rios wrote:
    I consider EmployeeNotFound an exceptional situation, thus I would not use it on the interface. The approach does raises some concerns on the design of the interfaces, since it can easily be wronly used to create a chatty design (meaning many bad things). This isn't a concern if one uses it to only server different responses a given request might have. The problem appears where the responses are reporting progress on a process, and the receiving application might use those intermediate responses to activate other process.

     

    I agree that this can be exceptional situation for the service but I can't agree that this is an exception situation for the messaging infrastructure.

     

    SOAP faults can only be used with Soap11 and Soap12 related message versions. If you are using None as your message version you don't have SOAP faults. I expect the same to happen in WCF3.5 with REST and syndication.

     

    In my opinion, there should be a service access facade to process the messages (and transport/messaging exceptions) a return the objects created from the messages or throwing the appropriate exceptions.

    Wednesday, August 8, 2007 8:47 AM
  • abs. agree the messaging should be hidden

    Wednesday, August 8, 2007 8:53 AM
  • I came across this post via a link on Udi Dahan's blog and I must say it is a fascinating discussion.  Hopefully, you guys don't mind a "nobody" injecting his thoughts into this discussion. Smile


    It is my personal opinion that exceptions should only be used when exceptional circumstances have occurred and not for conveying business-level errors.  Exceptional situations are usually tied to the infrastructure (transport, host failures, etc) and should be considered service implementation details.  As someone that develops business integration solutions, I work hard to define my business services in ways that do not expose the underlying implementation details to consumers.

     

    We expose the functionality of our services through a series of interface contracts.  An interface contract represents a binding agreement that describes how to communicate with the service using a well-defined business language (represented by the schemas, messages, ports, etc.)  The problem with propagating runtime exceptions, in my opinion, is that they are an infrastructure implementation detail of the service - an artifact of the runtime or programming environment if you will.  If the service is implemented with COM+, the errors are handled internally using error codes/HRESULTS; services implemented in Java or C# will favour exceptions generated from business objects.  These implementation details should not concern the consumer. 

     

    Also consider that not all service consumers support SOAP-Fault to runtime exception mapping.  This is beyond the scope of the SOAP specification, so you can't rely on consumers having this convenient feature.  So what looks like a good model from your service's point of view might not be quite so elegant on the client-side.  What looks clean could end up being a nightmare for clients that have to manually parse and translate the contents a SOAP Fault's <detail> node into meaningful information.  While most modern SOAP frameworks are capable of translating programming-language exceptions from/into SOAP Faults, there are always special cases to consider.  Furthermore, there is a disconnect between exceptions thrown from code created by the service implementor and runtime exceptions thrown by the underlying messaging framework.  How is a consumer able to tell the difference between a business-level exception (EmployeeNotFound), a communications error (HttpException), or a framework runtime error (StackOverflowException).  This can lead to processing ambiguity at the consumer's end.  This lends weight to the argument that SOAP Faults should be reserved for exceptional circumstances and that they not be associated with the business process in any way.

     

    I maintain that a service consumer does not need to know the specifics of an exceptional business-level error, only that an exceptional situation occurred during execution of the request.  The service consumer should only care that the business request failed due to an exceptional circumstance.  Remember, we are talking about exposing services to distributed consumers, not locally hosted, in-proc API frameworks.  In my opinion, if a runtime exception is truly important enough to be propagated to the client, it should be integrated into the business contract.  This transforms the exception from an implementation detail to part of the business language.  Whether the exception becomes a status code or a full fledged business message (e.g. ServiceUnavailable) comes down to business requirements, personal preference, or well established internal policies/domain rules. 

     

    Another problem is that Web services developers frequently fall into the mindset of thinking of business services in terms of method calls instead of messages.  With that mindset, marshalling runtime exception to the service consumer, a.k.a "the caller", seems to makes sense.  Unfortunately, this type of thinking can really constrain your design and implementation.  A Web service should be thought of as one of potentially many conduits for exposing your business services.  Business processes require a much higher level of thought and design as they are inherently more complex than simple in-proc components or API frameworks.  Don't define your business process in-terms of your Web services; define your Web services in-terms of your business process.  Web services are merely one way of exposing business processes.

     

    If you must propagate business-level exceptions using SOAP faults, I highly recommend applying the Exception Shielding (or similar) pattern.  This isolates consumers from the specifics of a runtime exception and offers them a more consistent approach to handling errors.  For instance, you could translate all exceptions into a message that contains an errorTicketId, timestamp, severity, and a short, sanitized, description of the error.  The specifics of the exception (a stack trace for instance) are written to a secured location by the service for use by support desk personnel or administrators.  This is virtually all a consumer needs to handle exceptional situations through automated or manual, human-based, channels.  The Web service code is an implementation of an endpoint.  A Web service should delegate business process functionality to a business component, which can use implementation-dependent features, such as runtime exceptions.  Essentially, a Web service follows the Adapter pattern in that it can translate implementation nuances into something more neutral and consumer friendly.

     

    The disclaimer to my rant is that there is usually no magical one-size-fits-all solution that will satisfy the needs of all situations.  Sometimes propagating programming-language or implementation-dependent exceptions makes sense and is the correct thing to do.  Personally, I think it is more appropriate to start by defining the use-cases of an exposed service, the failure conditions, and attempting to incorporate the well-known failure conditions as business messages.  Don't even consider the implementation details, such as programming language/runtime environment, until you have a well defined view of the service/business process and its interface contract.  This will enable you to implement your service with a much greater degree of flexibility, specifically with regards to MEXs, sync/async, runtime environment, etc.

     

    Just my two-cents.

    Friday, August 10, 2007 1:46 AM