views:

176

answers:

3

In our production environment, our WCF services are serialized with the XMLSerializer. To do so our service interfaces have the [XMLSerializerFormat] attribute. Now, we need to change to DataContractSerializer but we must stay compatible with our existing clients. Therefore, we have to expose each service with both serializers.

We have one constraint: we don't want to redefine each contract interface twice, we have 50 services contract interfaces and we don't want to have

IIncidentServiceXml 
IIncidentServiceDCS
IEmployeeServiceXml 
IEmployeeServiceDCS
IContractServiceXml 
IContractServiceDCS

How can we do that?


More info

This is a description of what we have tried so far but I'm willing to try completely different approaches:

We tried to create all the endpoints by code in our own ServiceHostFactory class. Basically we create each endpoint twice. The problem is that at runtime, WCF complains that the service has two endpoints with the same contact name but with different ContractDescription instances. The message says we should use different contract names or reuse the same ContractDescription instance.

Other attempt:

We also tried to do it by using different Namespaces for each ContractDescription instance. That way we would keep the same contract interface (IIncidentService) but with two different namespaces:

http://ourcompany/XML/IIncidentService
http://ourcompany/DCS/IIncidentService

With that we were able to get farther but the service crashed with a weird exception:

An ExceptionDetail, likely created by IncludeExceptionDetailInFaults=true, whose value is:
System.InvalidOperationException: An exception was thrown in a call to a WSDL export extension: System.ServiceModel.Description.XmlSerializerOperationBehavior
contract: http://ourcompany.cs/XML:IUserServiceWCF ----> System.NullReferenceException: Object reference not set to an instance of an object.
   at System.ServiceModel.Description.XmlSerializerMessageContractExporter.ExportFaults(Object state)
   at System.ServiceModel.Description.MessageContractExporter.ExportMessageContract()
   at System.ServiceModel.Description.XmlSerializerOperationBehavior.System.ServiceModel.Description.IWsdlExportExtension.ExportContract(WsdlExporter exporter, WsdlContractConversionContext contractContext)
   at System.ServiceModel.Description.WsdlExporter.CallExtension(WsdlContractConversionContext contractContext, IWsdlExportExtension extension)
A: 

Short answer is, you can't, for exactly the reason your error message said, you can't have too endpoints with the same name which effectively you are trying to do. I think you will have to do exactly what you say you don't want to.

This might be your only option

The problem is that to specify a service is to use the XmlSerializer you need to declare the [XmlSerializerFormat] attribute on the service or the contract. Well since we want to use the same for both endpoints we can’t place it there, so we are left with placing it on the contract. However, when it boils down to it, both endpoints are using the same service and vicariously the same contract right?

Well, it doesn’t have to be so. You could have a contract A derive from contract B, then have the service implement contract A such that everything in both contracts is part of the service. For this example though, contract B will be our standard contract, and contract A will be an interface that just defines the [XmlSerializerFormat] attribute.

But I can't promise you that will work with your existing clients code without changes.

Serapth
@Serapth: I have updated my question above with another attempt
Sly
A: 

What about having two services implementing the same contract?

Like:

class DcsService : Service
{}

[XmlSerializerFormat] 
class XmlService : Service
{}

class Service : IServiceContract
{}

Never worked with XmlSerializer but we used this construct for other purposes.

Alex
A: 

Just create your object using the [DataContract] and [DataMember] tags. The XmlSerializer and DataContractSerializer will both serialze the object just fine. They updated The XmlSerializer in the 3.0 framework to handle datacontract serialization. DataContractSerializer can handle [Serializable] objects but the behavior is not exact and takes some tweaking.

Just make all of your objects using Data Contracts. This way you don't have to worry about making two calls (one for each). You can use either XmlSerializer or DataContractSerializer with no problems.

If you need to add behavoir attributes you can always add both [Serializable] and [DataContract] if you like.

[Serializable]
[DataContract]
public class Customer

{
    [DataMember]
    public int Age { get; set; }

    [DataMember]
    public string Name { get; set; }

    [DataMember]
    public int Number { get; set; }

    [DataMember]
    public string FullName { get; set; }

    [XmlIgnore]
    public int IgnoredNumber { get; set; }
}

XmlSerializer Serializes as:

<?xml version="1.0" encoding="utf-16" ?> 
<Customer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;
  <Age>88</Age> 
  <Name>Bob</Name> 
  <Number>808</Number> 
  <FullName>Bob Jones</FullName> 
  </Customer>

DataContractSerializer Serializes as:

  <?xml version="1.0" encoding="utf-8" ?> 
<Customer xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/ConsoleApplication1"&gt;
  <Age>88</Age> 
  <FullName>Bob Jones</FullName> 
  <Name>Bob</Name> 
  <Number>808</Number> 
  </Customer>

You can obviously Force the Namespace so they both Match Exactly. This is just for example purposes.

CkH