11

I've created two identical web api projects, one in VS 2012 and another in VS 2013, both targeting the 4.5 .net framework. The projects are based on Filip W's video download tutorial found here: http://www.strathweb.com/2013/01/asynchronously-streaming-video-with-asp-net-web-api/

Copying & pasting the code from the tutorial into the VS 2012 project (using web api 1?) produces no errors (after I add the proper 'using' statements).

However, when I follow the same steps in the VS 2013 project I get the following two errors:

Error 1
The call is ambiguous between the following methods or properties: 'PushStreamContent(System.Func<Stream,HttpContent,TransportContext,Task>, MediaTypeHeaderValue)' and 'PushStreamContent(System.Action<System.IO.Stream,HttpContent,TransportContext>, MediaTypeHeaderValue)'

Error 2
'void video_stream.Controllers.VideoStream.WriteToStream(System.IO.Stream, System.Net.Http.HttpContent, System.Net.TransportContext)' has the wrong return type

So my guess is error 2 is the real problem as this code:

public async void WriteToStream(Stream outputStream, HttpContent content, TransportContext context) {...}

Is not identified as an <action> anymore between web api 1 & 2?? I'm really confused here as I'm targeting the same framework, and I can't seem to make the intuitive leap on how to fix it. My attempts at changing the WriteToStream signature have all failed.

Does anybody have a clue on what I need to get PushStreamContent to accept WriteToStream in web api 2 or VS 2013 or the new C# or where ever the difference in this code lives?

Ian Kemp
  • 28,293
  • 19
  • 112
  • 138
huxley
  • 295
  • 1
  • 3
  • 13

5 Answers5

14

The signature of the PushStreamContent constructor changed. Its onStreamAvailable parameter is an Action or Func generic type. The trouble is that the compiler doesn't know which type to bind to.

So to resolve the error cast the streamAvailableHandler as an Action:

response.Content = new PushStreamContent((Action<Stream, HttpContent, TransportContext>)streamAvailableHandler);

And the handler method would be:

private void streamAvailableHandler(Stream stream, HttpContent content, TransportContext context) {
  ...write to stream
}
Bill Heitstuman
  • 979
  • 1
  • 8
  • 23
10

I am not sure if this is a bug in Web API, we will investigate into it. Meanwhile you can try the following workaround:

response.Content = new PushStreamContent(async (Stream outputStream, HttpContent content, TransportContext context) =>
{
    try
    {
        var buffer = new byte[65536];

        using (var video = File.Open(filename, FileMode.Open, FileAccess.Read))
        {
            var length = (int)video.Length;
            var bytesRead = 1;

            while (length > 0 && bytesRead > 0)
            {
                bytesRead = video.Read(buffer, 0, Math.Min(length, buffer.Length));
                await outputStream.WriteAsync(buffer, 0, bytesRead);
                length -= bytesRead;
            }
        }
    }
    finally
    {
        outputStream.Close();
    }
});

Note: I made another change(removed the catch block) to the code to allow exceptions to propagate. This is so that your clients know that some error happened at the service otherwise they would assume everything went smooth.

Kiran
  • 56,921
  • 15
  • 176
  • 161
  • I eventually got to your exact solution late last night while looking at a different PushStreamContent example I found. This prompted me to "Go to Definition" in both VS 12 & 13, which I guess I should have done from the start. In 12 there are 3 overloads for PSC, but in 13 there are 6 overloads. Looking closely it appears to pass an external method an extra param is now required (Task) that wasn't needed before. I can confirm the above solution does work. – huxley Nov 18 '13 at 18:47
5

This is a known issue with the C# spec. Check out this SO question - Compiler Ambiguous invocation error - anonymous method and method group with Func<> or Action

When we introduced this overload that returns a Task, we did realize that it is a source level breaking change (though it doesn't break binary compatibility). We still went ahead with the change as not fixing it would cause more issues.

And regarding how to fix it, you have two options -

  1. As Kiran suggested earlier, you can use the anonymous method syntax so that the compiler picks the right overload for you.
  2. You can use an explicit cast, like below,

    response.Content = new PushStreamContent((Action)video.WriteToStream, new MediaTypeHeaderValue("video/"+ext));

BTW, be careful with that async void method. I suggest you to change its signature to

public async Task WriteToStream(Stream outputStream, HttpContent content, TransportContext context)
Community
  • 1
  • 1
RaghuRam Nadiminti
  • 6,718
  • 1
  • 33
  • 30
0

As sujjested by RaghuRam Nadiminti, by changing the WriteToStream's return type from void to Task, compilation will run successfully and you won't need the explicit casting.

public async Task WriteToStream(Stream outputStream, HttpContent content, TransportContext context);

-1

This was my workaround: if the call was ambiguous, just cast it:

response.Content = new PushStreamContent(
    (Action<Stream, HttpContent, TransportContext>) video.WriteToStream,
new MediaTypeHeaderValue("video/" + ext));
Kelly Keller-Heikkila
  • 2,544
  • 6
  • 23
  • 35
reiherric
  • 19
  • 5