WCF returning a custom object with a collection of

2019-06-11 00:20发布

问题:

I don't know if this could be done, but I have a WCF service that should return a custom object, the object has a collection of another custom object that contains a stream.

when I try to return this object I get

System.Runtime.Serialization.InvalidDataContractException: Type 'System.ServiceModel.Dispatcher.StreamFormatter+MessageBodyStream' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. See the Microsoft .NET Framework documentation for other supported types.

If I change to method to just return one of the stream with Stream as return type it works fine. It would be too much code for me to post, so I was just wondering in general if it's possible, and if there is somethings special I have to do to get custom object with streams to return without errors from WCF service?

I Use wsHttpBindig now while testing.

I have marked the streams and the IList as DataMembers in the classes, should I mark them something else?

Thanks for any help, if it's not understandable I can try to create a smal example code

回答1:

Do you actually want streaming to happen, or do you just want it serialized (and are ok with it being buffered)?

If you're ok with it being buffered:

Keep in mind that the DataContractSerializer does not have built-in support for Streams, but it does for byte arrays. So, do the usual DataContract type conversion trick: Don't mark the stream with DataMember, but create a private [DataMember] property of type byte[] that wraps the Stream. Something like:

public Stream myStream;

[DataMember(Name="myStream")]
private byte[] myStreamWrapper {
   get { /* convert myStream to byte[] here */ }
   set { /* convert byte[] to myStream here */ }
}

If you actually want it streamed:

The WCF ServiceModel can only support a streamed message body if the Stream is the entire body. So, your operation should return a MessageContract that returns all the non-Stream things as headers. Like so:

[MessageContract]
public class MyMessage {
   [MessageHeader]
   public MyDataContract someInfo;
   [MessageBody]
   public Stream myStream;
}


回答2:

In short: you can't mix buffered transfer (sending back int, string or custom complex types marked as DataContracts) with streaming.

Well documented here: MSDN on WCF Streaming.

It says:

Restrictions on Streamed Transfers

Using the streamed transfer mode causes the run time to enforce additional restrictions.

Operations that occur across a streamed transport can have a contract with at most one input or output parameter. That parameter corresponds to the entire body of the message and must be a Message, a derived type of Stream, or an IXmlSerializable implementation. Having a return value for an operation is equivalent to having an output parameter.

So I guess you'll have to rearchitect your solution to have two methods - one that returns the basic information in a complex type, and a second operation that handles the streaming.

Marc



回答3:

This seems to be a duplicate question - see my answer to WCF returning a custom object with a collection of custom objects containing streams for a solution.

By the way

When you use Stream directly in an [OperationContract], it's a special case. The DataContractSerializer isn't even invoked. The WCF ServiceModel uses a special way to write out the message body using the stream (ensuring that it is actually streamed, if the underlying binding supports it).

But when you use Stream as just another [DataMember] inside a [DataContract], it's just another type to the DataContractSerializer, and an unsupported one at that. So you have to use the type conversion trick (see my earlier link).

Unintuitive, I know :) But documented to some degree here and here.