Is removing a MessageBodyMember in a MessageContract considered a breaking change? - c#

Consider the following ServiceContract-Interface:
[ServiceContract]
public interface ITest
{
[OperationContract]
void MyMethod(MyClass obj);
}
With MyClass beeing:
[MessageContract]
public MyClass
{
[MessageBodyMember(Order = 0)]
public int A { get; set; }
[MessageBodyMember(Order = 1)]
public int B { get; set; }
[MessageBodyMember(Order = 2)]
public int C { get; set; }
}
MyClass is now changed to look like the following:
[MessageContract]
public MyClass
{
[MessageBodyMember(Order = 0)]
public int A { get; set; }
[MessageBodyMember(Order = 2)]
public int C { get; set; }
}
Would a client consuming this WCF-Service need to make additional changes to work with the new service definition?
Additionaly, what would happen if I were to additionally change C to have the new Order = 1?

If the client updates the WSDL File, it gets a syntax error in the code of the client, when the client calls the method.
The order element set on which position in the communication the bodymember sending to the server/client. You can see it into the svc log. Example.:
<ns2: myClass xmlns:ns2="yourNamespace">
<A xmlns=""></A>
<B xmlns=""></B>
<C xmlns=""></C>
</ns2:myClass>
After changed the ordner element:
<ns2: myClass xmlns:ns2="yourNamespace">
<C xmlns=""></C>
<A xmlns=""></A>
<B xmlns=""></B>
</ns2: myClass >
I have tried the example for you. I have used a WCF C# Web service and a C# client with a standard protocol: BasicHttpBinding. And I use the WCF Test client. In this combination I got no error in the client, provided that the client makes no WSDL update. In this case, you can change the order element without errors. But this is not a correct implementation; therefore you can’t assume that everything is working. For other clients see the result might be different. For example, a Java client is much more restrictive.

Adding to #naro's answer:
It is not a breaking change for the following frameworks:
.NET (C#, 4.5)
Java 8
I did not test anything else as it doesn't matter for our scenario.
This means every client still works (without regenerating the client) even after updating the web service definition.

Related

Mimicing Inheritance in Protobuf-net v2

So I have a .NET server and an IOS client and I'm trying to use protocol buffers to communicate back and forth. On the server I have one object that contains a list of FormField objects where FormField is actually going to be a subtype of FormField like say ButtonFormField.
After doing some research, I implemented my .proto files similar to this: Protobuf-net .proto file generation for inheritance
Which works fine for serializing from the server to the client.
Unfortunately when I send items back to the server, I get errors because it is trying to cast FormField as ButtonFormField (which obviously doesn't work) in CompiledSerializer.cs line 49.
Does this not work in protobuf-net v2 or am I doing something wrong? I am really hoping to get this working.
EDIT*
I tried this using protobuf-net v1 and it does work, so I'm assuming this behavior changes in v2. Is there an alternate way to do this in v2 or am I stuck on v1 for now?
Edit 2:
Sorry for the delay Marc. Here is a cut down .proto and the C# classes. I am using protoc to generate the IOS classes, so I am assuming generating C++ or Java with protoc as the client would generate the same effect also.
Working Sample Proto File
message PersonSelectionFormField {
}
message TextFormField {
}
message FormFieldDto {
//Properties and optional properties truncated for brevity
required int32 Id = 1;
required int32 FieldTemplateId = 6;
required int32 RowId = 9;
optional PersonSelectionFormField PersonSelectionFormField = 55;
optional TextFormField TextFormField = 59;
}
Classes
namespace Sample.Fields
{
//I get a list of FormFields back from the client
[ProtoContract]
[ProtoInclude(55, typeof(PersonSelectionFormField))]
[ProtoInclude(59, typeof(TextFormField))]
public class FormField:IFormField
{
//Cleaned up for brevity...There are virtual methods and fields
//getting overriden but they aren't relevant to the example
[ProtoMember(1)]
public int Id { get; set; }
[ProtoMember(6)]
[Required]
public int FieldTemplateId { get; set; }
[ProtoMember(9)]
[Required]
public int RowId { get; set; }
}
public class TextFormField:FormField
{
//Some overriden field / methods.
//None of the serialized fields are overriden
}
public class PersonSelectionFormField:FormField
{
//Some overriden field / methods.
//None of the serialized fields are overriden
}
}

Returning List<T> with WCF service

I got an Employee class and each employee has a list of applied leaves. Is it possible to have the list AppliedLeave as a [DataMember] in WCF?
[DataContract]
public class Employee
{
[DataMember]
public string UserID { get; set; }
[DataMember]
public int EmployeeNumber { get; set; }
[ForeignKey("EmployeeUserID")]
[DataMember]
public List<Leave> AppliedLeave
{
get { return _appliedLeaves; }
set { _appliedLeaves = value; }
}
private List<Leave> _appliedLeaves = new List<Leave>();
...
}
Is there any other way to do this?
thank you for your consideration of this matter
I extend my Question
This is my Leave Class:
[DataContract]
public class Leave
{
[Key()]
[DataMember]
public Guid LeaveId { get; set; }
[DataMember]
public string LeaveType { get; set; }
[DataMember]
public DateTime StartDate { get; set; }
[DataMember]
public string EmployeeUserID { get; set; }
}
this shows ServiceContract ---->
[ServiceContract]
public interface IEmployeeService
{
[OperationContract]
Employee GetEmployeeByUserId(string userId);
[OperationContract]
void AssignSupervisor(string userId, string supervisorUserId);
[OperationContract]
void DeleteEmployeeByUserId(string userId);
....
}
In Client application,
EmployeeServiceClient employeeService = new EmployeeServiceClient();
Employee employee = employeeService.GetEmployeeByUserId(id);
But when Employee gathered from the service its shows Null for leaves,
Can somebody help me? what have I done wrong here?
Yes, it is possible to return generics from WCF service operations.
But by default they are casted to Array on client side. This can be customized while proxy generation.
WCF: Serialization and Generics
Also you have to decorate the service with all the types to which generics can be resolved, using KnownTypeAttribute.
Known Types and the Generic Resolver
I also found my server side list would always arrive at the client as a null pointer. After browsing around a lot for this problem it strikes me it is nearly always denied at first ("your code should work")
Found the issue.. I had configured my solution using one "WCF Service" project and one "Winforms app" project with a generated service reference. Both interface and implementation of Service1 were in the WCF service project, as expected. But any list member returned null.
When I put my IService1.cs = the interface only = in a separate class library instead, reference the class library on both sides (using) and generate the service reference again, my list does work ! The generated code on the client side looks much simpler.
I did not need any special attributes, change service reference configuration, or interface references for this.
You could use IList<T> instead of List<T>.

WCF MessageHeaderArray not working correctly

I have a relatively simple service that takes in a Message contract for uploading a file. Because it is a file upload, only the file stream can be in the message body so I am using the message header for meta data.
My problem is that although I've added a string[] as a MessageHeaderArray when I add a service reference to the service in a test web app client, the variable has been generated as just a string.
Here is part of my MessageContract:
[MessageContract]
public class FileInformation : IDisposable
{
[MessageHeader(MustUnderstand = true)]
public string FileName { get; set; }
[MessageHeaderArray]
public string[] RequiredEntityNames { get; set; }
[MessageHeaderArray]
public string[] RequiredEntityIds { get; set; }
[MessageHeader(MustUnderstand = true)]
public string EntityName { get; set; }
This is driving me mad and I have spent almost a day trying to figure out what's going on. Any ideas on why the RequiredEntityNames and RequiredEntityIds are being generated as string instead of string[]?
Have you tried with "MessageHeader" attribute on arrays instead of "MessageHeaderArray" ?
MSDN, "Using Arrays Inside Message Contracts" : http://msdn.microsoft.com/en-us/library/ms730255.aspx

WCF: generic list serialized to array

So I am working with WCF and my services return types that contain generic lists. WCF is currently converting these to arrays over the wire. Is there a way I configure WCF to convert them back to lists afterwards? I know there is a way by clicking advanced when you add a service reference but I am looking for a solution in configuration files or something similar.
[DataContract(IsReference = true)]
public class SampleObject
{
[DataMember]
public long ID { get; private set; }
[DataMember]
public ICollection<AnotherObject> Objects { get; set; }
}
It is very odd, also, because one service returns it as a list and the other as an array and I am pretty sure they are configured identically.
At the advanced tab when adding your service reference you can set this option as well. standard Arrays are set.
I think this is dent with purely from the way that the client tool generates the contracts from the WSDL. In my case, I made a reusable .dll that contains my [OperationContract] and [DataContract] classes, and use it from both the client and the server, instead of generating one with SvcUtil. This way I preserve my lists of generics.
In addition, take care not to have both arrays and generics in the classes from which you serialize the instances with WCF, because you'll get a problem during deserialization : everything will be converted either to ArrayOf (if you don't change the configuration) or to Collection Type.
As result you will get errors during deserialization from the WCF code trying to assign an array where you wait a Collection and conversely.
This was just my 2cent advice from what I learned during a small project with WCF. :)
I found a solution that was much simpler and worked well enough for me, although it might not work for others. I simply switched from using ICollection (IList also produces this result) to List. It worked fine after that.
Solution from Here.
I also found a possible configuration solution from Here near the bottom.
<CollectionMappings>
<CollectionMapping TypeName="ChangeTracker.ChangeTrackingCollection'1" Category="List" />
</CollectionMappings>
Instead of use ICollection<AnotherObject> in your data contract, that will be generated in client application as a AnotherObject[].
Try this:
define a new data contract
[CollectionDataContract]
public class AnotherObjectCollection : List<AnotherObject> {}
in your code:
DataContract(IsReference = true)]
public class SampleObject
{
[DataMember]
public long ID { get; private set; }
[DataMember]
public AnotherObjectCollection Objects { get; set; }
}
in Visual Studio (same to svcUtil), the client proxy code will appear like this:
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]
[System.Runtime.Serialization.CollectionDataContractAttribute(Name="AnotherObjectCollection", Namespace="http://schemas.datacontract.org/2004/07/SampleObject", ItemName="AnotherObject")]
[System.SerializableAttribute()]
public class AnotherObjectCollection : System.Collections.Generic.List<AnotherObject> {}
DataContract(IsReference = true)]
public class SampleObject
{
[DataMember]
public long ID { get; private set; }
[DataMember]
public AnotherObjectCollection Objects { get; set; }
}
This also works for built-in .NET types.
antonio

Silverlight/Web Service Serializing Interface for use Client Side

I have a Silverlight solution that references a third-party web service. This web service generates XML, which is then processed into objects for use in Silverlight binding. At one point we the processing of XML to objects was done client-side, but we ran into performance issues and decided to move this processing to the proxies in the hosting web project to improve performance (which it did). This is obviously a gross over-simplification, but should work. My basic project structure looks like this.
Solution
Solution.Web - Holds the web page
that hosts Silverlight as well as
proxies that access web services and
processes as required and obviously
the references to those web
services).
Solution.Infrastructure - Holds
references to the proxy web services
in the .Web project, all genned code
from serialized objects from those
proxies and code around those objects
that need to be client-side.
Solution.Book - The particular
project that uses the objects in
question after processed down into
Infrastructure.
I've defined the following Interface and Class in the Web project. They represent the type of objects that the XML from the original third-party gets transformed into and since this is the only project in the Silverlight app that is actually server-side, that was the place to define and use them.
//Doesn't get much simpler than this.
public interface INavigable
{
string Description { get; set; }
}
//Very simple class too
public class IndexEntry : INavigable
{
public List<IndexCM> CMItems { get; set; }
public string CPTCode { get; set; }
public string DefinitionOfAbbreviations { get; set; }
public string Description { get; set; }
public string EtiologyCode { get; set; }
public bool HighScore { get; set; }
public IndexToTabularCommandArguments IndexToTabularCommandArgument { get; set; }
public bool IsExpanded { get; set; }
public string ManifestationCode { get; set; }
public string MorphologyCode { get; set; }
public List<TextItem> NonEssentialModifiersAndQualifyingText { get; set; }
public string OtherItalics { get; set; }
public IndexEntry Parent { get; set; }
public int Score { get; set; }
public string SeeAlsoReference { get; set; }
public string SeeReference { get; set; }
public List<IndexEntry> SubEntries { get; set; }
public int Words { get; set; }
}
Again; both of these items are defined in the Web project. Notice that IndexEntry implments INavigable. When the code for IndexEntry is auto-genned in the Infrastructure project, the definition of the class does not include the implmentation of INavigable. After discovering this, I thought "no problem, I'll create another partial class file reiterating the implmentation". Unfortunately (I'm guessing because it isn't being serialized), that interface isn't recognized in the Infrastructure project, so I can't simply do that. Here's where it gets really weird. The BOOK project CAN see the INavigable interface. In fact I use it in Book, though Book has no reference to the Web Service in the Web project where the thing is define, though Infrastructure does. Just as a test, I linked to the INavigable source file from indside the Infrastructure project. That allowed me to reference it in that project and compile, but causes havoc in the Book project, because now there's a conflick between the one define in Infrastructure and the one defined in the Web project's web service. This is behavior I would expect.
So, to try and sum up a bit. Web project has a web service that process data from a third-party service and has a class and interface defined in it. The class implements the interface. The Infrastructure project references the web service in the Web Project and the Book project references the Infrastructure project. The implmentation of the interface in the class does NOT serialize down, so the auto-genned code in INfrastructure does not show this relationship, breaking code further down-stream. The Book project, whihc is further down-stream CAN see the interface as defined in the Web Project, even though its only reference is through the Infrastructure project; whihc CAN'T see it.
Am I simple missing something easy here? Can I apply an attribute to either the Interface definition or to the its implmentation in the class to ensure its visibility downstream? Anything else I can do here?
I know this is a bit convoluted and anyone still with me here, thanks for your patience and any advice you might have.
Cheers,
Steve

Categories

Resources