1

I am using some code adapted from Lowy's ServiceModelEx to package an exception inside a FaultException, send it across the wire, and then unpack it on the client as a .NET Exception. My server can throw exceptions of theoretically any kind, but let's use a SqlException as an example.

Lowy uses reflection to unpack, so the code needs to examine the type of the exception to determine whether it can construct a valid object:

    static Exception ExtractException(ExceptionDetail detail)
    {
        Exception innerException = null;
        if (detail.InnerException != null)
        {
            innerException = ExtractException(detail.InnerException);
        }
        Type type = Type.GetType(detail.Type);
        Debug.Assert(type != null, "Make sure this assembly contains the definition of the custom exception");
        Debug.Assert(type.IsSubclassOf(typeof(Exception)));

        //...
    }

Two questions

  1. With SqlExceptions, that first Debug.Assert fails. Trying to get them to resolve on the client, I have set a reference to System.Data, but no luck. I'm assuming this is because, without any actual System.Data invocation in that client, the compiler removes the reference? What can I do, in the short term, to get my client to resolve that .GetType call?

  2. What is the correct way to "rethrow" the LambdaException as a base ExceptionDetail without losing granularity of detail (e.g. StackTrace) and without needing any assembly reference on the client side? Is this even possible?

EDIT: this is the source that wraps the ExceptionDetail as a FaultException on the server. Again, this is mostly from Lowy's ServiceModelEx:

    public static void PromoteException(Type serviceType, Exception error, MessageVersion version, ref Message fault)
    {
        //Is error in the form of FaultException<T> ? 
        if (error.GetType().IsGenericType && error is FaultException)
        {
            Debug.Assert(error.GetType().GetGenericTypeDefinition() == typeof(FaultException<>));
            return;
        }

        ExceptionDetail exdetail = new ExceptionDetail(error);
        FaultException<ExceptionDetail> faultException = new FaultException<ExceptionDetail>(exdetail);

        MessageFault messageFault = faultException.CreateMessageFault();
        fault = Message.CreateMessage(version, messageFault, faultException.Action);

        //...
    }
tom redfern
  • 30,562
  • 14
  • 91
  • 126
downwitch
  • 1,362
  • 3
  • 19
  • 40
  • You shouldn't need to reference `System.Data`. Using `Type.GetType(detail.Type, true)` should produce a more informative error. – Michael Liu Jan 18 '13 at 16:50
  • What exception was thrown by `Type.GetType`? (I'm trying to understand why the assert fails in question 1. I'll let someone else address question 2.) – Michael Liu Jan 18 '13 at 16:58
  • @MichaelLiu Apologies, that was completely false--a competing initiative here had swapped out that exception type on me. Error returned is "Could not load type 'System.Data.SqlClient.SqlException' from assembly '...'" Have removed my other misleading comments. – downwitch Jan 18 '13 at 17:40
  • What assembly is it trying to load SqlException from? What is the entire content of `detail.Type`? – Michael Liu Jan 18 '13 at 17:45
  • @MichaelLiu My assembly, that's the fully typed name represented by ... detail.Type is `System.Data.SqlClient.SqlException`, as expected – downwitch Jan 18 '13 at 17:54
  • You didn't post the code that initializes `detail.Type`, but it needs to include the assembly info as well: "System.Data.SqlClient.SqlException, System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=...". – Michael Liu Jan 18 '13 at 18:04
  • @MichaelLiu Added source. Not sure why that assembly info is not included. – downwitch Jan 18 '13 at 18:10
  • How does the `ExceptionDetail` constructor initialize the `Type` property? – Michael Liu Jan 18 '13 at 18:12
  • @MichaelLiu metadata says it is "the type string for the exception passed to the constructor". Read-only. – downwitch Jan 18 '13 at 18:20
  • It looks like `ExceptionDetail` doesn't preserve the assembly information in the type string. So I don't think this approach will work for CLR exceptions, only for exceptions defined in your own assembly. – Michael Liu Jan 18 '13 at 18:38
  • @MichaelLiu I follow your reasoning but 1) the type fails if I revert to using Lowy's original `Exception` object, too, so it's not an `ExceptionDetail` problem, it seems, but a wire problem and 2) if I catch the SqlException and throw an `ApplicationException` instead it works as expected, so it isn't only for assembly-defined exceptions. – downwitch Jan 18 '13 at 20:09
  • It works for `Exception` and `ApplicationException` because `Type.GetType` looks in both the currently executing assembly and in mscorlib.dll (where `Exception` and `ApplicationException` are declared) if no assembly is specified in the type string. It doesn't work for types declared in any other assembly. – Michael Liu Jan 18 '13 at 20:13
  • @MichaelLiu Right, I see that. That sounds like at least part of the answer then: make sure exceptions coming through the wire are either of executing assembly- or mscorlib-declared types. So it's down to Q2 now, and whether just, say, catching the `SqlException` and throwing a more generic type is a wise practice or not. – downwitch Jan 18 '13 at 20:16

0 Answers0