Getting bad generated code from "Update Service Reference"
Asked Answered
S

2

1

In VB.NET (using Visual Studio 2008) my WCF service has an interface something like:

<ServiceContract()> _
Public Interface IThingService
    <OperationContract()> _
    Function GetThingByNumber(ByVal thingNumber As MyKeyClass) As Thing
    <OperationContract()> _
    Function GetThing(ByVal thingId As Guid) As Thing

    ' ...

End Interface

I recently changed two projects with similar code to use a basicHttpBinding rather than a wsHttpBinding. Everything compiles well on the service side. Now, in the a client app I choose "Update Service Reference". In one project, my resulting reference.vb seems correct--under 100 lines with simple wrappers for each method. However, in the other, the resulting reference.vb can't seem to understand what the service is. I get a reference.vb of over 1000 lines that looks like:

 '------------------------------------------------------------------------------
 ' <auto-generated>
 '     This code was generated by a tool.
 '     Runtime Version:2.0.50727.3053
 '
 '     Changes to this file may cause incorrect behavior and will be lost if
 '     the code is regenerated.
 ' </auto-generated>
 '------------------------------------------------------------------------------
 Option Strict On
 Option Explicit On
 Imports System.Data
 Namespace ThingService

 <System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0"),  _
 System.ServiceModel.ServiceContractAttribute(ConfigurationName:="GetThingByVersion.IGetThingByVersion")>  _
 Public Interface IThingService

    'CODEGEN: Parameter 'GetThingByNumberResult' requires additional schema information that cannot be captured using the parameter mode. The specific attribute is 'System.Xml.Serialization.XmlElementAttribute'.
    <System.ServiceModel.OperationContractAttribute(Action:="http://tempuri.org/ThingService/GetThingByVersion", ReplyAction:="http://tempuri.org/ hingService/GetThingByVersionResponse"),  _
     System.ServiceModel.XmlSerializerFormatAttribute()>  _
    Function GetThingByNumber(ByVal request As ThingService.GetThingByVersionRequest) As ThingService.GetThingByVersionResponse

    'CODEGEN: Parameter 'GetThingResult' requires additional schema information that cannot be captured using the parameter mode. The specific attribute is 'System.Xml.Serialization.XmlElementAttribute'.
    <System.ServiceModel.OperationContractAttribute(Action:="http://tempuri.org/ThingService/GetThing", ReplyAction:="http://tempuri.org/ThingService/GetThingResponse"),  _
     System.ServiceModel.XmlSerializerFormatAttribute()>  _
    Function GetThing(ByVal request As ThingService.GetThingRequest) As ThingService.GetThingResponse
'...
End Interface

 '''<remarks/>
 <System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "2.0.50727.3082"),  _
 System.SerializableAttribute(),  _
 System.Diagnostics.DebuggerStepThroughAttribute(),  _
 System.ComponentModel.DesignerCategoryAttribute("code"),  _
 System.Xml.Serialization.XmlTypeAttribute([Namespace]:="http://schemas.datacontract.org/2004/07/ThingLibraryCore")>  _
 Partial Public Class MyKeyClass
    Inherits Object
    Implements System.ComponentModel.INotifyPropertyChanged

    Private concatenatedThingNumberField As String
    Private ThingNumberField As Integer
    Private ThingNumberFieldSpecified As Boolean

 '... goes on and on...

It's as if the code generated knows nothing of my actual service interface. Any idea how to troubleshoot this? Thanks in advance.

EDIT: Looks like I need to make sure that the server can use the DataContractSerializer and not the XmlSerializer: see http://blogs.msdn.com/sonuarora/archive/2007/06/16/contract-generation-from-wsdl-xml-schema-datacontractserializer-vs-xmlserializer.aspx . Does anyone know how I can figure out what in my code (probably in Class Thing) is violating the restrictions on DataContractSerializer?

Stichometry answered 18/8, 2009 at 14:30 Comment(6)
Look at the comments in the generated code. They're telling you what's wrong.Fizgig
I don't understand them. What is "parameter mode"? Where is it supposed to be getting the schema information? What is XmlElementAttribute supposed to tell me? I'm googling around to no avail.Stichometry
Notice, for example, that GetThing(Guid) is in the interface, but the generated client has GetThing(String). I can't figure that part out either.Stichometry
Does anyone know how I can figure out what in my code (probably in Class Thing) is violating the restrictions on DataContractSerializer?Stichometry
I was right: there was a property in Thing that the DataContractSerializer couldn't handle, so it fell back to using the XmlSerializer, which generated unusable schema/client code. The property in question inherited from the generic Dictionary(Of string, OtherClass). I would use the KnownTypeAttribute to overcome this, but I can't because it attributes don't seem to work with generics. I am going to create a wrapper for Dictionary(Of string, OtherClass) rather than inherit from it.Stichometry
The wrapper didn't seem to coax the DataContractSerializer into kicking in.Stichometry
H
3

Honestly, I'm not sure what the answer is. Have you tried deleting the service reference and re-creating it from scratch? That would seem to be the most straightforward way to try to fix it, especially since you've made changes.

I know you didn't ask this, but as an aside, I have personally gotten away from using the service reference feature in visual studio altogether. Here is an excellent video that describes how easy it is to do, providing you are willing to refactor your code a little bit. Since it sounds like you are in charge of both WCF client and server, I think you'd benefit tremendously from the approach Miguel advocates.

EDIT:

In response to John Saunder's comment, if you are in charge of both the client and the server, you'd be better off, in my opinion, to re-factor the contracts (service and data contracts) into a single assembly that is shared between the client and server. When you add/update a service reference, all that does is make a code-generated copy of these contracts for the client and then adds the boilerplate code for the proxy. So in essence, you have two separate, but identical, definitions of these interfaces and classes, one on the server side and one on the client side.

The reason I migrated from doing this is because I have a WCF service hosted in a Windows service. This service was used by clients in four separate assemblies throughout my project. Every time I made a change to the service/data contract for the WCF service, I had to go update the service reference in the four assemblies that used the WCF service. By refactoring these contracts to a single, shared assembly, I update that assembly, re-compile (which I would have to do anyway), and I'm ready to go. I no longer have to remember which assemblies need to be updated.

I realize that many of the examples on the web talk about the simplicity of using the svcutil tool, but in my case, it's unnecessary overhead.

Take a look at the video and judge for yourself.

Heterosporous answered 18/8, 2009 at 21:53 Comment(5)
I would recommend against this, without a much better reason. Almost all the examples you'll find on the web will be using service references.Fizgig
Add Service Reference permits you to use types from shared assemblies. Just click the "Advanced" button.Fizgig
Thanks, Matt--that video was very helpful. Even if I do the "simple" manual client creation (using the ChannelFactory method), I think we will have issues with the way things serialize when dealing with a Dictionary(Of string, TVal). I plan to start simple and go from there.Stichometry
@John Saunders, I'll check it out. But even with that, I still have to update the client proxy in each and every location the service is "used" when the interface changes. Re-factoring everything into a single assembly allows me to bypass this headache.Heterosporous
The video is a good one. I now also recommend avoiding Service References wherever you can ship a new DLL when the service contract changes.Stichometry
B
0

Make sure all your datacontract contains only those properties which has return type as either primitive data type or any other dataContact. Datatype such as DataSet and DataType which requires XMLSerialization will convert your datacontract to MessegeContract and code generator simply displays the same messege as comment.

Bornstein answered 30/6, 2014 at 9:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.