WCF Client Code Generation - Issue with "Reuse types from referenced assemblies" option in Add Service Reference

I believe, there are some questions around "Resuse types from referenced assemblies" option under "Add Service Reference" in Visual Studio. Recenty, had the opportunity to invetigate it and here's what we found:

When attempting to generate the client side code from the service metadata, and using the "Reuse types from referenced assemblies" option, both svcutil and Add Service Reference run into the following issue.

If the types you are attempting to reuse have the following structure, an exception is thrown when generating client code:

namespace ModelToReuse
{
[DataContract(IsReference = true)]
public class Bar
{
[DataMember]
public Foo<Bar> fooBar;
}

    [DataContract(IsReference = true)]
public class Foo<T>
{
}
}

You see the following errors:

Attempting to download metadata from 'https://localhost:56757/Service1.svc' using WS-Metadata Exchange or DISCO.
Error: Cannot import wsdl:portType
Detail: An exception was thrown while running a WSDL import extension: System.ServiceModel.Description.DataContractSerializerMessageContractImporter
Error: Referenced type 'ModelToReuse.Foo`1, ModelToReuse, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' with data contract name 'FooOfBarhckbnXhe' in namespace 'https://schemas.datacontract.org/2004/07/ModelToReuse' cannot be used since it does not match imported DataContract. Need to exclude this type from referenced types.
XPath to Error Source: //wsdl:definitions[@targetNamespace='https://tempuri.org/']/wsdl:portType[@name='IService1']

Error: Cannot import wsdl:binding
Detail: There was an error importing a wsdl:portType that the wsdl:binding is dependent on.
XPath to wsdl:portType: //wsdl:definitions[@targetNamespace='https://tempuri.org/']/wsdl:portType[@name='IService1']
XPath to Error Source: //wsdl:definitions[@targetNamespace='https://tempuri.org/']/wsdl:binding[@name='BasicHttpBinding_IService1']

Error: Cannot import wsdl:port
Detail: There was an error importing a wsdl:binding that the wsdl:port is dependent on.
XPath to wsdl:binding: //wsdl:definitions[@targetNamespace='https://tempuri.org/']/wsdl:binding[@name='BasicHttpBinding_IService1']
XPath to Error Source: //wsdl:definitions[@targetNamespace='https://tempuri.org/']/wsdl:service[@name='Service1']/wsdl:port[@name='BasicHttpBinding_IService1']

Generating files...
Warning: No code was generated.
If you were trying to generate a client, this could be because the metadata documents did not contain any valid contracts or services or because all contracts/services were discovered to exist in /reference assemblies. Verify that you passed all the metadata documents to the tool.

Root Cause:  

Internally, the tool [that generates the WCF client proxy code] does not picks up the "IsReference=true" parameter on the data contract attribute when processing the referenced contract (the type defined in the dll). This leads the tool to conclude that the reference contract (which it believes has IsReference=false) does not match the data contract (which has IsReference=true), which makes it not be able to reuse that type. Thus, it throws an exception and does not generate the proxy code correctly.

Resolution:

This will be fixed in post visual studio 2010 release, until then please use one of the workarounds below.

Workarounds:

1. Don’t “Reuse types in referenced assemblies” (simply uncheck this box in the Add Service Reference advanced dialog)

a. Pros: This is a very simple workaround

b. Cons: Customer gets the functionality they want, but they lose all their data types (don’t have access to all the types’ methods, can't do type comparisons, etc.).

2. Set IsReference to false for all data types

a. Pros: Simple workaround, and customer gets to reuse all their types from “ModelToReuse.dll”

b. Cons: If the customer ever attempts to serialize an object with a circular reference (object A refers to object B which refers to object A), they will get a serialization exception or a stack

overflow exception.

3. Use svcutil, with its “Reuse types” option as well as its “Exclude Type” parameter

a. The command I used is as follows:

svcutil.exe https://localhost:23195/SLService1.svc /r:"C:\Users\avnermay\Desktop\ClientFailsToGenerateProxy\ModelToReuse\bin\Debug\ModelToReuse.dll" /et:"Core.Model.Incident.INC_SubItem`1, ModelToReuse, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"

You should be able to do the equivalent thing in ASR – check out Lifeng’s blog:

https://blogs.msdn.com/lifenglu/archive/2007/08/13/side-by-side-comparsion-between-svcutil-and-the-service-reference-in-vs.aspx .

b. Pros: This gives the customer basically all the behavior they want. They can reuse all their types except for INC_SubItem<T>.

c. Cons: They need to use svcutil instead of Add Service Reference. More importantly, they are unable to use the INC_SubItem<T> data type defined in their referenced ModelToReuse.dll assembly. Instead svcutil generates the code for this type, which is what they must use on the client side.

4. Set IsReference to false for all data types, and add a behavior which returns a DataContractSerializer with preserveObjectReferences set to true in its constructor

a) The general idea is that you create a DataContractSerializer for both your service and your client which treats all data types as if they have IsReference set to true.

b) This way, svcutil will not run into the type mismatch problem, and the customer gets all the behavior they want.

c) Pros: The customer gets all the behavior they want. They can reuse all their data types and circular references are handled correctly by DataContractSerializer.

d) Cons: The most complicated workaround to implement. Requires adding this behavior to both client and server.

e) Here are the steps for this workaround:

(i) Remove "IsReference = true" parameter on all data contracts (or set it to false, the default value).

(ii) Create class that derives from "DataContractSerializerOperationBehavior"

(iii) Override "CreateSerializer" to return a DataContractSerializer that sets PreserveObjectReference to true in its constructor

(iv) Add this operation behavior to both the service and the client. You can do this through code or through config.

 

More Information:

Repro Steps via Add Service Reference
1. Open the attached dev10 solution
2. Add Service Reference to Service1.svc

Repro Steps via svcutil
1. Open solution
2. Set WcfService1 as Startup Solution
3. Start service without debugging (ctrl+F5)
4. Run svcutil with the /r option to reuse types from ModelToReuse.dll. This is the command I used:
svcutil.exe https://localhost:56757/Service1.svc /r:"C:\Users\avnermay\Documents\Visual Studio 2010\Projects\WcfService1\ModelToReuse\bin\Debug\ModelToReuse.dll"

Expected Results
1. Client proxy is generated successfully

Actual Results
1. Client proxy is not generated successfully.
2. You get the following errors (both in svcutil and in Add Service Reference)

Attempting to download metadata from 'https://localhost:56757/Service1.svc' using WS-Metadata Exchange or DISCO.
Error: Cannot import wsdl:portType
Detail: An exception was thrown while running a WSDL import extension: System.ServiceModel.Description.DataContractSerializerMessageContractImporter
Error: Referenced type 'ModelToReuse.Foo`1, ModelToReuse, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' with data contract name 'FooOfBarhckbnXhe' in namespace 'https://schemas.datacontract.org/2004/07/ModelToReuse' cannot be used since it does not match imported DataContract. Need to exclude this type from referenced types.
XPath to Error Source: //wsdl:definitions[@targetNamespace='https://tempuri.org/']/wsdl:portType[@name='IService1']

Error: Cannot import wsdl:binding
Detail: There was an error importing a wsdl:portType that the wsdl:binding is dependent on.
XPath to wsdl:portType: //wsdl:definitions[@targetNamespace='https://tempuri.org/']/wsdl:portType[@name='IService1']
XPath to Error Source: //wsdl:definitions[@targetNamespace='https://tempuri.org/']/wsdl:binding[@name='BasicHttpBinding_IService1']

Error: Cannot import wsdl:port
Detail: There was an error importing a wsdl:binding that the wsdl:port is dependent on.
XPath to wsdl:binding: //wsdl:definitions[@targetNamespace='https://tempuri.org/']/wsdl:binding[@name='BasicHttpBinding_IService1']
XPath to Error Source: //wsdl:definitions[@targetNamespace='https://tempuri.org/']/wsdl:service[@name='Service1']/wsdl:port[@name='BasicHttpBinding_IService1']

Generating files...
Warning: No code was generated.
If you were trying to generate a client, this could be because the metadata documents did not contain any valid contracts or services or because all contracts/services were discovered to exist in /reference assemblies. Verify that you passed all the metadata documents to the tool.

Warning: If you would like to generate data contracts from schemas make sure touse the /dataContractOnly option.