I have a very simple WCF 4.0 service in which I am returning an POCO entity object whose IsReference is set to true. When I return the object from my service, the values of primitive types (int, double, etc) are set correctly and the ones having references are also getting serialized properly, I've verified that the serialization happen correctly putting following code just before return statement. I've also verified that the members are correctly marked with [DataMember] and they have public getters/setters. However, when the object is received by the caller, all the primitive types are set to 0 and the ones with references are set to NULL.
Would appreciate any help!
try
{
var stream = new MemoryStream();
var dataContractSerializer = new DataContractSerializer(onlineSellItemDetail.GetType());
dataContractSerializer.WriteObject(stream, onlineSellItemDetail);
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException.ToString());
}
This is almost always because no XML, empty XML, or incompatible XML was received on the client end. Sometimes, this is because the stream was not flushed or closed properly on the service side. Other times, the client side is operating with completely valid XML in its own stream, but is reading the XML by perhaps not starting at the beginning of the stream. If you enable tracing on the service side and the client side, generate tracing logs, and analyze with SvcTraceViewer, and in parallel, use Fiddler to monitor the wire traffic on both the client side and the service side, I think you'll find the answer for sure!
Related
I am setting up a .NET core service that is reading from RabbitMQ using Rebus. It seems that the request placed in RabbitMQ needs to have the .NET object namespace information. Is there a way to work around this. For example if I had a service written in Python placing items on the queue would it be possible to read and process these requests. It seems every time I test and try to send something besides the .NET object I get an exception.
System.Collections.Generic.KeyNotFoundException: Could not find the key 'rbs2-content-type' - have the following keys only: 'rbs2-msg-id'
It depends on which serializer, you're using in the receiving end.
By default, Rebus will use its built-in JSON serializer with a fairly "helpful" setting, meaning that all .NET types names are included. This enables serialization of complex objects, including abstract/interface references, etc.
This serializer requires a few special headers to be present, though, e.g. the rbs2-content-type header, which it uses to verify that the incoming message presents itself as JSON (most likely by having application/json; charset=utf-8 as its content type).
If you want to enable deserialization of messages from other platforms, I suggest you provide the necessary headers on the messages (which – at least with Rebus' built-in serializer – also includes a .NET type name of the type to try to deserialize into).
Another option is to install a custom serializer, which is a fairly easy thing to do – you can get started by registering your serializer like this:
Configure.With(...)
.(...)
.Serialization(s => s.Register(c => new YourCrazySerializer()))
.Start();
which you then implement somewhat like this:
public class YourCrazySerializer : ISerializer
{
public async Task<TransportMessage> Serialize(Message message)
{
var headers = message.Headers.Clone();
// turn into byte[] here
//
// possibly add headers
return new TransportMessage(headers, bytes);
}
public async Task<Message> Deserialize(TransportMessage transportMessage)
{
var headers = transportMessage.Headers.Clone();
// turn into object here
//
// possibly remove headers
return new Message(headers);
}
}
As you can see, it's pretty easy to modify Rebus to accept messages from other systems.
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 am creating a WCF webservice whose requests/responses are supposed to be signed only.
For this, on ServiceContract attribute I have set
ProtectionLevel = ProtectionLevel.Sign
That works ok.
Due to requirements some SoapFaults are supposed to be thrown from service; two types of SoapFaults:
related to application
related to WS-Addressing (e.g. MessageID is missing)
For this I am using the normal of approach of dealing with SoafFaults: create an IErrorHandler in which a Message instance is created with MessageFault.CreateFault.
Almost all the returned SoapFaults are not encrypted (which is ok for me),
my question is why the ones with action="http://www.w3.org/2005/08/addressing/fault" or "http://www.w3.org/2005/08/addressing/soap/fault" are encrypted?
Check out http://msdn.microsoft.com/en-us/library/aa347791.aspx and http://msdn.microsoft.com/en-us/library/system.servicemodel.faultcontractattribute.aspx.
It states that
If you select a binding that enables security and you do not set the
ProtectionLevel property anywhere on the contract, all application
data will be encrypted and signed.
I guess that the build in types by default use this behaviour. You can verify this by looking at which exception is actually thrown.
Can a WCF service return an instance of an anonymous type or do they need to be a class? My service returns JSON for the website to consume. It would be nice to take a page from MVC and just return something like this and have the service automatically serialize it into JSON.
return new { user = data, lastUpdate = time, success = true };
You can't ever return an object of an anonymous type from any method. Not and have much useful happen with it.
The problem is not specific to WCF. It just gets worse in WCF since WCF has to worry about generating metadata from your service contract. Remember that the service contract isn't the part that decides to return JSON, so WCF can't do anything special for this particular method.
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