What's the best way to handle adding a new (optional) parameter to an existing operation without requiring the client to update their WSDL? I do not want to update the namespace to describe a new version of the service contracts, since this should be backwards compatible with older clients.
Should I add a new operation with a new parameter, as an overload? Or should I just add the parameter to the existing operation?
Here is my operation:
[OperationContract]
MyResponse GetData();
Should it be:
[OperationContract]
MyResponse GetData();
[OperationContract]
MyResponse GetData(string filter);
Or more simply, just change it to this:
[OperationContract]
MyResponse GetData(string filter);
The latter option seems best, and according to my reference book, "The impact on client is none. New parameters are initialized to default values at the service." Is WCF initializing it to the the so-called default value? If so, what is the default value?
One thing to take into consideration is that you can't have two OperationContracts with the same name. The way it's serialized it will throw an error.
The best approach is to go with Option 3 (just adding the new parameter) and within the method logic account for it being a null value for those clients that haven't updated yet. If it's a breaking change that the clients will need to update for, make sure to not have the entire application die because of the exception.
Well, changing an existing contract after it's been used is really against all rules of service orientation; you should never ever break an existing contract.
In reality, this happens quite frequently, and WCF is pretty good about handling that for you. As long as you only introduce non-breaking changes, existing clients will continue to work.
This can be:
a new operation contract on an existing service contract
a new non-required field on a DataContract
What you're trying to do is not going to work, though
you cannot have two method with the same name in WCF - WCF is not .NET and you cannot have two methods by the same name being different only by their signature. Doesn't work. You'll need to use two separate, distinct names. Remember: your WCF method calls will be translated into a WSDL (web service description language) document to describe the service - and WSDL simply does not support having two operations with the same name - just a difference in signature is not supported and will not work.
you cannot change the existing contract, e.g. you cannot introduce a new parameter into a method call after the fact, without breaking the contract.
So what you really need to do is this:
[OperationContract]
MyResponse GetData();
[OperationContract]
MyResponse GetFilteredData(string filter);
Any other change you suggested will a) break the contract, or b) simply not work in WCF:
you can try this:
[OperationContract]
MyResponse GetData();
[OperationContract(Name = "GetDataByFilter")]
MyResponse GetData(string filter);
Related
I have a WCF service that has an operation that takes any .net serializable client data.
[OperationContract]
void SaveMyData(long id, string name, object serializableData);
[OperationContract]
object LoadMyData(long id, string name);
The server doesn't need to know what the data is, it just stores it or returns what is stored. And the server doesn't even know the types being serialized so of course this contract would result in deserialization exceptions.
I know that I could serialize/deserialize this independently of the WCF contract, for example:
[OperationContract]
void SaveMyData(long id, string name, byte[] serializedData);
[OperationContract]
byte[] LoadMyData(long id, string name);
But this requires additional code on the client to serialize and deserialize. I'd like to avoid that and have the client code as simple as possible.
I know that I could create a pre-build proxy in a client dll that would wrap the WCF calls and perform the additional serialization/deserialization. But I'd rather be able to rely on the clients generated from the WSDL.
Ideally, a RawAttribute could be placed on the parameters or return value which would suppress serialization/deserialization (of the universal root object type) and instead supply or expect an (object)byte[] (or (object)Stream?) from the operation.
[OperationBehavior]
public void SaveMyData(long id, string name, [Raw] object serializableData){ ... }
[OperationContract, Raw]
object LoadMyData(long id, string name);
I've looked at DataContractSurrogate and DataContractResolver but I'm not seeing how to achieve this. DataContractSurrogate seems too late in the deserialization pipeline as the type and deserialized object are already supplied. The resolver doesn't give the data, just the type info. Neither gives information about the parameter being deserialized for which to find the RawAttribute.
Does WCF offer an appropriate extensibility point for this? Or a built-in way?
I would also like to know what the declared type is, as extracted from the serialized data, but that isn't necessary.
Thanks!
Instead to fighting WCF's serialization mechanism, you should drop one level of abstraction and work at the message level of WCF. What you're looking for is a kind of "universal" service that can accept messages from any client. Read through this old but still applicable MSDN article on WCF Messaging. Toward the bottom of the article (figure 8) is sample code for a generic WCF service. That should at least give you a start in creating a service that bypasses serialization.
I've read this post regarding enumaration changes between versions, but it didn't help me.
I have the following wcf service:
[ServiceContract]
public interface IService1
{
[OperationContract]
MyEnum Foo();
}
[DataContract]
public enum MyEnum
{
[EnumMember]
first,
[EnumMember]
Second,
}
I'm looking for a way to add a new enum member, only to the service side. Let's say my client is using an old version of the proxy, without the new enum member I want to add.
My goal is avoiding a serialization exception, I want my client to be smart enough to handle this situation, ignoring the new value or anything. Any Ideas?
According to the answer in the question that you linked to, adding a new element to the enum does not break compatibility.
What will crash is sending an enum value to a client that does not have that value in the enum list.
To fix this by only changing the server side:
Add new value to enum
Create new service method that does the same as the old
New clients will use the new service
The old clients will use the old service
In the old service, before the result is sent, check if one of the new enum values are being used, if so change it to one of the old ones ("Uknown" if you have that value)
This may be more work than it is worth, it depends on how many clients you have and how difficult it is to update them.
If I need to go from this service contract:
[ServiceContract(Namespace="http://api.x.com/Svc1")]
public interface IService1
{
[OperationContract(Name = "AddCustomer")]
bool AddCustomer(DTOCustomer1 customer);
}
to this:
[ServiceContract(Namespace="http://api.x.com/Svc1")]
public interface IService1
{
[OperationContract(Name = "AddCustomer")]
bool AddCustomer(DTOCustomer2 customer);
}
and according to this good article: Versioning WCF I understand that when data contract is changed there is a need of defining a new vs of data contract in new namespace followed by defining a new vs of service contract in new namespace, after which a new endpoint should be added.
How exactly am I suppose to have this done. Is there an example anywhere? Could you write something based on my service contract shown above?
Thank you in advance!
According to the linked article you should do something like:
[ServiceContract(Namespace="http://api.x.com/Svc1")]
public interface IServiceNew : IService1
{
[OperationContract(Name = "AddCustomerNew")]
bool AddCustomer(DTOCustomer2 customer);
}
Then implement it in your service:
public class MyCurrentServiceImplementation : IServiceNew
{...}
You will need to redeploy your service but existing clients should be able to continue to call the AddCustomer operation, and new clients can call the AddCustomerNew operation.
It's very important to note that the assumption you state in your post:
"when data contract is changed there is a need of defining a new vs of
data contract in new namespace"
is not always true. See "Data Contract Versioning" on MSDN for a number of cases where a data contract change is non-breaking and may therefore require no action other than perhaps modifying the internal implementation of your service method to handle the presence/absence of certain data due to differences between data contract versions.
In this specific example I would question how two versions of a method called AddCustomer can vary so much in their intent that it justifies creating a new service interface. Without seeing your old and new data contracts I can't know for sure, but I'm guessing that the real issue here is that the method has evolved to accept additional customer information.
If that's true, then it's very much like the situation of optional arguments in a method call. WCF is designed to handle this scenario very nicely as a non-breaking change to the data contract. As long as you can follow the guidelines in "Best Practices: Data Contract Versioning" on MSDN, then calls supplying either the old or new version of the contract will be accepted just fine by your existing service interface. Your service method will get the data that is possible given the combination of the client and server data contracts.
I would keep my service interface coherent, simple, and clean (i.e. avoid doing things like IServiceNew) and instead just add to the data contract and modify the implementation of AddCustomer to adapt to the whatever data it receives.
I have an service Interface:
[ServiceContract]
[ServiceKnownType(typeof(Models.ArticleImage))]
public interface IPhotoManagementService
{
[OperationContract]
bool Login(string username, string password);
[OperationContract]
bool IsLoggedIn();
[OperationContract]
void UpdateImage(string articleID, string selectedImage);
}
As you can see I specify a typeof(Models.ArticleImage) on my ServiceContract.
So building the WSDL of this service should cause ArticleImage to pop up in the WSDL. Unfortunarly this doesn't happen at all. Why is that?
ArticleImage has DataContract on it. And when I return an ArticleImage in my interface, then the WSDL does pick up ArticleImage.
Edit: it doesn't even pop up in the service reference in the consuming project!
This is the result of a lot of testing:
The model I'm trying to add is a LINQ to SQL model.
When I add a normal model with ServiceKnownType it works.
When I use my LINQ to SQL entities in my Interface it works.
When I add my LINQ to SQL entity through ServiceKnownType it doesn't pop up.
Only types used as input/output parameters of service contract operations are published in the WSDL.
Why would it need to? Where does your service expose something that could possibly be an ArticleImage?
Re your comment; when using [ServiceKnownType], the extra trype is still exposed in the "mex" (consumed via "svcutil") - but not by the WSDL. Are you using a WCF client? It should appear (I've just checked... it did). In general, though, returning vague data from a web-service isn't a great idea... sub-types, sure! Dictionary<string,ArticleImage> or even Dictionary<string,SomeBaseType> (with [KnownType] etc), fine! But object, HashTable, etc - aren't a good idea (IMO).
You might also just return a list of your type (List<ArticleImage>) which will work in all scenarios (and be easy for WSDL etc); and let the client make the dictionary at their end.
With regards to LINQ-to-SQL; objects for "mex" need to be decorated with [DataContract] / [DataMember]. You can do this in the designed by toggling the "serialization" property for the dbml. With this set (Serialization Mode = Unidirectional), it should work. To be honest, though, I think you be better-off just adding a dummy method that makes the type explicit on the API.
I'm using WCF and want to upload a large file from the client to the server. I have investigated and decided to follow the chunking approach outlined at http://msdn.microsoft.com/en-us/library/aa717050.aspx
However, this approach (just like streaming) restricts the contract to limited method signitures:
[OperationContract(IsOneWay=true)]
[ChunkingBehavior(ChunkingAppliesTo.InMessage)]
void UploadStream(Stream stream);
The sample uses the rather convenient example of uploading a file from a fixed path and saving it to a fixed path on the server. Therefore, my question is how do I pass additional parameters to specify things like filename, filepath etc.
eg. I would like something like:
[OperationContract(IsOneWay=true)]
[ChunkingBehavior(ChunkingAppliesTo.InMessage)]
void UploadStream(Stream stream, String filePath);
Thanks in advance,
Mark.
This article explains how to use the MessageHeader attribute to force things to be passed in the header, and therefore not count as a parameter. So, instead of passing a stream and other meta data, create a class that has the attribute MessageContract and mark all of the meta data as a MessageHeader. Then, mark the stream as a MessageBodyMember (which the article incorrect calls "MessageBody"). Have your UploadStream method take a single parameter whose type is that of the MessageContract class you've just created. I've done this successfully, but I haven't done it in tandem with chunking. Good luck.
You could make your service session-ful and have an initialization method in the contract with the IsInitiating property set to true. Something like:
[OperationContract(IsInitiating = true)]
void InitializeUploadService(string filename);
[OperationContract(IsOneWay = true, IsInitiating = false)]
[ChunkingBehavior(ChunkingAppliesTo.InMessage)]
void UploadStream(Stream stream);
I have never tried it with streaming services but it should basically make WCF enforce that InitializeUploadService is always called before UploadStream.
More documentation can be found here:
http://msdn.microsoft.com/en-us/library/system.servicemodel.description.operationdescription.isinitiating.aspx
I would look at MessageContracts and add those values as message headers to your object. This should allow you to pass the stream and any values related to the stream as message headers.
Setting up the maxItemsInObjectGraph in the Client side and Server side worked for me.
(Dont forget the client side.) http://social.msdn.microsoft.com/Forums/en/wcf/thread/0af69654-2d89-44f3-857a-583b57844ca5