DTOs on the client, returning binary data as a stream, and this lame error

1,016 views
Skip to first unread message

David Nichol

unread,
Mar 9, 2012, 1:05:52 PM3/9/12
to servic...@googlegroups.com
Hi, there. I'm evaluating ServiceStack right now. I like it very much and appreciate its opinionated nature. I have a few questions, however.

I'm trying to create a simple service to store and retrieve binary resources. My resource DTO looks like this:

public class Resource : IRequiresRequestStream
{
public Guid ResourceID { get; set; }
public string ContentType { get; set; }
public DateTime? ExpirationDate { get; set; }
public Stream Data { get; set; }

public Stream RequestStream
{
get { return Data; }
set { Data = value; }
}
}

Here's some client code:

var id = Guid.NewGuid();
var client = new JsonRestClientAsync(baseUri);
var filestream = File.OpenRead("C:\\Temp\\ResourcesTest\\client\\2048401invoice.pdf");
var resource = new Resource
  {
  ContentType = "application/pdf",
  Data = filestream,
  ResourceID = id
  };

client.PutAsync<Resource>(relativeOrAbsoluteUrl: string.Format("/resource/{0}", id.ToString("N")),
 onSuccess: response => Console.WriteLine("Success"),
 onError: (response, ex) => Console.WriteLine(ex.ToString()),
 request: resource);

}

And I'm getting this error: "Timeouts are not supported on this stream." (I'll copy the callstack and details at the bottom of this message). Does anyone know why? It seems to be originating from the client? It's trying to serialize my stream, it seems. Do I even want it to do that?

On a related note, what is the recommended way to deal with the fact that the client (I'm using the C# client) needs the same DTOs as the server? Is it advisable to use a common assembly, or have the DTOs defined separately?

Thanks! See the error below.

System.InvalidOperationException: Timeouts are not supported on this stream.
   at System.IO.Stream.get_ReadTimeout()
   at lambda_method(Closure , Stream )
   at ServiceStack.Text.Common.WriteType`2.WriteProperties(TextWriter writer, Ob
ject value) in C:\src\ServiceStack.Text\src\ServiceStack.Text\Common\WriteType.c
s:line 201
   at ServiceStack.Text.Common.WriteType`2.WriteAbstractProperties(TextWriter wr
iter, Object value) in C:\src\ServiceStack.Text\src\ServiceStack.Text\Common\Wri
teType.cs:line 178
   at ServiceStack.Text.Common.WriteType`2.WriteProperties(TextWriter writer, Ob
ject value) in C:\src\ServiceStack.Text\src\ServiceStack.Text\Common\WriteType.c
s:line 216
   at ServiceStack.Text.JsonSerializer.SerializeToStream(Object value, Type type
, Stream stream) in C:\src\ServiceStack.Text\src\ServiceStack.Text\JsonSerialize
r.cs:line 148
   at ServiceStack.Text.JsonSerializer.SerializeToStream[T](T value, Stream stre
am) in C:\src\ServiceStack.Text\src\ServiceStack.Text\JsonSerializer.cs:line 135

   at ServiceStack.ServiceClient.Web.JsonRestClientAsync.SerializeToStream(IRequ
estContext requestContext, Object dto, Stream stream) in C:\src\ServiceStack\src
\ServiceStack.Common\ServiceClient.Web\JsonRestClientAsync.cs:line 38
   at ServiceStack.ServiceClient.Web.AsyncServiceClient.RequestCallback[T](IAsyn
cResult asyncResult) in C:\src\ServiceStack\src\ServiceStack.Common\ServiceClien
t.Web\AsyncServiceClient.cs:line 285


Andy McGoldrick

unread,
Mar 9, 2012, 1:13:36 PM3/9/12
to servic...@googlegroups.com
Hi David

have you treid making your DTO look like:

public class Resource : IRequiresRequestStream
{
public Guid ResourceID { get; set; }
public string ContentType { get; set; }
public DateTime? ExpirationDate { get; set; }
public byte[] Data { get; set; }

public  byte[]  RequestStream
{
get { return Data; }
set { Data = value; }
}
}

I can't see how you can serialize a stream, which is in effect just a pipeline of data, I reckon you would need to read the contents into a byte[] and then serialize the entire content.

Andy

Arxisos

unread,
Mar 9, 2012, 2:22:19 PM3/9/12
to servic...@googlegroups.com
Hi David,

ServiceStack needs to serialize the stream, so Andy's suggestion makes sense.

You also don't want to use IRequiresRequestStream, because this interface indicates that the request DTO shouldn't be serialized at the server side:

public class Resource
{
public Guid ResourceID { get; set; }
public string ContentType { get; set; }
public DateTime? ExpirationDate { get; set; }
public byte[] Data { get; set; } //Populate this array from the stream
}

But it's not recommended to handle file uploads as normal requests, see http://stackoverflow.com/questions/8296867/servicestack-client-add-attachment for more information.

David Nichol

unread,
Mar 9, 2012, 3:55:19 PM3/9/12
to servic...@googlegroups.com
That's interesting. So ServiceStack doesn't support using a stream for binary data? Only for JSON/XML data?

I'll try it using a byte array. 

Thanks-

Demis Bellot

unread,
Mar 9, 2012, 4:30:50 PM3/9/12
to servic...@googlegroups.com
If you want to upload a file I also recommend using the noraml HTTP File Upload, i.e. see how the RestFiles ( http://www.servicestack.net/RestFiles/#!files ) example handles HTTP uploads: 

Client:

Server:

You can also upload by adding a byte[] Property on your Request DTO however this is less ideal with the text serializers since it's base64 encoded.

Although if you use the ProtoBuf plugin the byte[] properties don't get unencoded so ends up being much faster:

Cheers,

David Nichol

unread,
Mar 12, 2012, 9:37:36 AM3/12/12
to servic...@googlegroups.com
So the recommended way of uploading files uses POST only? I would like to use PUT for binary files as well if possible.

Thanks-

Demis Bellot

unread,
Mar 12, 2012, 9:49:59 AM3/12/12
to servic...@googlegroups.com
POST is recommended since that's how HTTP File Upload currently works on the web.
But feel free to do the same using PUT, here's the source code for an UploadFile helper for WebRequest using the multipart/form-data ContentType:

Reply all
Reply to author
Forward
0 new messages