开发者

Why does the proxy generated code create a new class when a MessageContract is in my WCF Service?

I have created two WCF Services (Shipping & PDFGenerator). They both, along with my ClientApp, share an assembly named Kyle.Common.Contracts. Within this assembly, I have three classes:

namespace Kyle.Common.Contracts
{
    [MessageContract]
    public class PDFResponse
    {
        [MessageHeader]
        public string fileName { get; set; }
        [MessageBodyMember]
        public System.IO.Stream fileStream { get; set; }
    }

    [MessageContract]
    public class PDFRequest
    {
        [MessageHeader]
        public Enums.PDFDocumentNameEnum docType { get; set; }
        [MessageHeader]
        public int? pk { get; set; }
        [MessageHeader]
        public string[] emailAddress { get; set; }
        [MessageBodyMember]
        public Kyle.Common.Contracts.TrackItResult[] trackItResults { get; set; }
    }

    [DataContract(Name = "TrackResult", Namespace = "http://kyle")]
    public class TrackResult
    {
        [DataMember]
        public int SeqNum { get; set; }
        [DataMember]
        public int ShipmentID { get; set; }
        [DataMember]
        public string StoreNum { get; set; }
    }
}

My PDFGenerator ServiceContract looks like:

namespace Kyle.WCF.PDFDocs
{
    [ServiceContract(Namespace="http://kyle")]
    public interface IPDFDocsService
    {
        [OperationContract]
        PDFResponse GeneratePDF(PDFRequest request);

        [Op开发者_高级运维erationContract]
        void GeneratePDFAsync(Kyle.Common.Contracts.Enums.PDFDocumentNameEnum docType, int? pk, string[] emailAddress);

        [OperationContract]
        Kyle.Common.Contracts.TrackResult[] Test();
    }
}

If I comment out the GeneratePDF stub, the proxy generated by VS2010 realizes that Test returns an array of Kyle.Common.Contracts.TrackResult. However, if I leave GeneratePDF there, the proxy refuses to use Kyle.Common.Contracts.TrackResult, and instead creates a new class, ClientApp.PDFDocServices.TrackResult, and uses that as the return type of Test.

Is there a way to force the proxy generator to use Kyle.Common.Contracts.TrackResult whenever I use a MessageContract? Perhaps there's a better method for using a Stream and File Name as return types?

I just don't want to have to create a Copy method to copy from ClientApp.PDFDocServices.TrackResult to Kyle.Common.Contracts.TrackResult, since they should be the exact same class.


After a lot of extra digging, I realize that it was actually the Enum that "broke" it. It has do with the way DataContractSerializer works vs. XmlSerializer. Long story short, the solution was to turn the Enum into a nullable.

[MessageContract]
public class PDFRequest
{
    [MessageHeader]
    public Enums.PDFDocumentNameEnum? docType { get; set; }
    [MessageHeader]
    public int? pk { get; set; }
    [MessageHeader]
    public string[] emailAddress { get; set; }
    [MessageBodyMember]
    public Kyle.Common.Contracts.TrackItResult[] trackItResults { get; set; }
}


I ran into the same problem (MessageContract+enums) and your post helped me. Indeed if you explicitly set the enum fields to nullable it works. The issue is that when enums are used, WCF uses the XML serializer which cannot tell null from empty string.

There is a detailed explanation of this behaviour here by one of the actual WCF team members.

In the case of document/literal when using bare messages, WCF falls back to XmlSerializer when handling enum types. ... XmlSerializer treats null as missing by default ... we encounter a schema without nillable="true" ... The detection logic for value types currently only handles primitive value types, it does not check for enums.

In other words WCF does not like enums... But hey, it works, you just need to be aware of it!


You can instruct Visual Studio to re-use classes from referenced assemblies. So if your test project has an assembly reference to the Kyle.Common.Contracts assembly, it should re-use those types defined in there rather than adding new client-side proxy classes.

The switch to enable this is on the Advanced page in the Add Service Reference dialog window - it should be on by default:

Why does the proxy generated code create a new class when a MessageContract is in my WCF Service?

Make sure that your project

  • has an assembly reference to the common data contract assembly
  • that this setting is really ON when you add the service reference
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜