This is quite a big limitation, as I should not be forced to update
all clients. Extra optional parameters added should not force the old
clients to be updated, especially if the service can guarantee full
backwards compatibility.
It is not a limitation at all because one, the service is not supposed to know about the client and two, the addition of operations/members are not considered breaking because the client need not know about the addition.
Contract changes in a service is considered "non breaking". The links to other similar problem on SO that you have shared are not exactly addressing your problem, which can be categorized into the following bullet points:
- Service Discovery
- Service Versioning
- Implement IExtensibleDataObject (if the problem is caused by the data/data type after a roundtrip with the new addition to the service)
1 & 2 are about the client discovering the service correctly, the client consuming your service needs to be aware of it for lax versioning compatibility ie., you need to confirm that the client is not performing any schema validation against the old service before even making a call to your service. If this be the case, then you need to use explicit XML namespaces and define new contracts and new service definitions.
This is not a limitation but keeping in with strict versioning plus it is also about the data types unknown to the earlier client binding with your service that faults with an exception due to a callback, which is reasonable and you should accept it has nothing to do with the SOA.
To use this solution, you may need to define your contract and service in the following way:
public interface IPurchaseOrderV1
{
string OrderId { get; set; }
string CustomerId { get; set; }
}
[DataContract(
Name = "PurchaseOrder",
Namespace = "http://examples.microsoft.com/WCF/2005/10/PurchaseOrder")]
public class PurchaseOrderV1 : IPurchaseOrderV1
{
[DataMember(...)]
public string OrderId {...}
[DataMember(...)]
public string CustomerId {...}
}
and another version for the newly added member like so,
public interface IPurchaseOrderV2
{
DateTime OrderDate { get; set; }
}
[DataContract(
Name = "PurchaseOrder",
Namespace = "http://examples.microsoft.com/WCF/2006/02/PurchaseOrder")]
public class PurchaseOrderV2 : IPurchaseOrderV1, IPurchaseOrderV2
{
[DataMember(...)]
public string OrderId {...}
[DataMember(...)]
public string CustomerId {...}
[DataMember(...)]
public DateTime OrderDate { ... }
}
For the source of this code, you may refer to this link which will definitely help you understand what is wrong with your service and how to modify it.
Just added this as an afterthought to isRequired attribute to a DataMember, which is false by default.
The below is referenced from here.
If a default value of null or zero for the member is unacceptable, a
callback method should be provided using the
OnDeserializingAttribute
to provide a reasonable default in case the
member is not present in the incoming stream.
[OnDeserialized]
public void OnDeserialized(StreamingContext context)
{
if (this.id == null) throw new ArgumentNullException("id");
if (this.Name == null) throw new ArgumentOutOfRangeException("name");
if (this.Salary < 0) throw new ArgumentOutOfRangeException("salary");
if (this.Salary > 0)
{
throw new InvalidOperationException("No child labor allowed");
}
}