Bug 7177 - Non generic derivations of the generic FaultException are not serialized correctly
Summary: Non generic derivations of the generic FaultException are not serialized corr...
Status: RESOLVED FIXED
Alias: None
Product: Class Libraries
Classification: Mono
Component: WCF assemblies ()
Version: master
Hardware: PC Windows
: --- normal
Target Milestone: Untriaged
Assignee: Martin Baulig
URL:
Depends on:
Blocks:
 
Reported: 2012-09-14 11:12 UTC by jens.hoffmann
Modified: 2012-09-19 00:12 UTC (History)
2 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on GitHub or Developer Community with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED FIXED

Description jens.hoffmann 2012-09-14 11:12:25 UTC
I just stumbled accross a problem using FaultExceptions with WCF. If the generic implementation of FaultException is derived by a non generic class it is not serialized correctly but instead an InternalServiceFault is sent.

small example:
---

[DataContract]
public class ArgumentFault
{
  [DataMember]
  public string ArgumentName
  {
    get;
    set;
  }
}

public class ArgumentFaultException
  : FaultException<ArgumentFault>
{
  public ArgumentFaultException(string message, string argumentName)
    : base(new ArgumentFault { ArgumentName = argumentName },
    new FaultReason(new FaultReasonText(
    message
      , new System.Globalization.CultureInfo("en-US"))),
            new FaultCode("ArgumentFault"))
  {}

  public ArgumentFaultException(string argumentName)
    : this(String.Format("The value of the argument {0} is invalid!", argumentName), argumentName)
  {}
}

---

The problem results from the code in line 159 of OperationInvokeHandler

     var fe = ex as FaultException;
159: if (fe != null && fe.GetType ().IsGenericType) {
       var t = fe.GetType ().GetGenericArguments () [0];
       foreach (var fci in mrc.Operation.FaultContractInfos)
         if (fci.Detail == t)
           return Message.CreateMessage (req.Version, fe.CreateMessageFault (), fci.Action);
     }

In the second line it checks whether the given fault is generic, which mine isn't.
Comment 1 Martin Baulig 2012-09-19 00:12:45 UTC
Fixed; master commit 37b26ae.

Note that you won't be able to get your custom exception type on the client side.  I actually just had to spend some time reading documentation and playing around with this on Windows to figure out what the exact rules are.

They are actually quite strict:

* You may _throw_ something that is derived from FaultException<T>, but that will be turned into FaultException<T>.

* The type T must be [DataContract] serializable and for each FaultException<T> that you throw, you must explicitly use [FaultContract (typeof (T))].

* You may not throw FaultException<U>, where U derives from T, without also using [FaultContract (typeof (U))].

Example (put this into your model dll):

        [DataContract]
        public class ArgumentFault
        {
                [DataMember]
                public string ArgumentName
                {
                        get;
                        set;
                }
        }

and this goes into your service library:

        [ServiceContract]
        public interface IMyService
        {
                [WebGet]
                [OperationContract]
                string Hello();

                [OperationContract]
                [FaultContract(typeof(ArgumentFault))]
                void TestException();
        }

        public class MyService : IMyService
        {
                public string Hello()
                {
                        return "World";
                }

                public void TestException()
                {
                        throw new FaultException<ArgumentFault>(new ArgumentFault { ArgumentName = "Test" });
                }
        }

Client code:


                static void Main()
                {
                        var client = new MyServiceClient();
                        var hello = client.Hello();
                        Console.WriteLine("HELLO: {0}", hello);

                        try
                        {
                                client.TestException();
                        }
                        catch (ArgumentFaultException ex)
                        {
                                Console.WriteLine("GOT CUSTOM ARGUMENT FAULT EX: {0}", ex.Message);
                        }
                        catch (FaultException<ArgumentFault> ex)
                        {
                                Console.WriteLine("GOT ARGUMENT FAULT EX: {0}", ex.Message);
                        }
                        catch (FaultException ex)
                        {
                                Console.WriteLine("GOT FAULT EX: {0}", ex.Message);
                        }
                        client.Close();
                }