System.NotSupportedException when trying to create an asset
Asked Answered
P

4

42

I am trying to use the Azure MediaService API along with the Azure Storage API in an API Service hosted in Azure.

The user sends the video stream to the service as an HttpPost, the service saves the video as a blob in my Storage account, the media service encodes the video and when the link to the video is ready it is returned to the user.

But when I am trying to create an Asset a System.NotSupportedException is thrown with the message:

Exception thrown: 'System.NotSupportedException' in Microsoft.Data.Services.Client.dll Exception thrown: 'System.NotSupportedException' in mscorlib.dll iisexpress.exe Error: 0 : Exception=System.NotSupportedException: This target framework does not enable you to directly enumerate over a data service query. This is because enumeration automatically sends a synchronous request to the data service. Because this framework only supports asynchronous operations, you must instead call the BeginExecute and EndExecute methods to obtain a query result that supports enumeration.

I am using the following versions of the required dependencies:

Microsoft.Data.Services.Client - 5.6.2.0
Microsoft.WindowsAzure.MediaServices.Client - 3.0.0.8
Microsoft.WindowsAzure.Storage - 3.1.0.1

Here is my code:

CloudMediaContext _context;
IAsset asset;
using (MemoryStream Ms = new MemoryStream(data.Data))
{
    _context = new CloudMediaContext("accountName", "accountKey");
    asset = await _context.Assets.CreateAsync("blobContainerName",
        AssetCreationOptions.None,CancellationToken.None);
    ...
    ...
}

The data.Data contains the byte[] of the video. The exception is thrown when CreateAsync is called. I tried _context.Assets.Create with no luck.

IMPORTANT EDIT

I created a new console application, used the code I am using in the API Service and it was executed successfully. So the problem is in the API Service.

Here is my class and method definitions

public class UploadController : ApiController
{

    [HttpPost]
    public async Task<string> PostUpload(VideoData data)
    {
        ...
        ...
    }

Any alternative to that maybe?

Pepita answered 31/5, 2015 at 2:53 Comment(12)
Hey, i'm not sure if ´await´ is here right in use. Did you look at azure.microsoft.com/de-de/documentation/articles/… ?Utu
Hello, thanks for your comment! I tried Create instead of CreateAsync with no luck.Pepita
It seems there is a bunch of operations behind the scene with Create(Async), maybe have a look at CreateEmptyAsset: blog-ndrouin.azurewebsites.net/creating-a-simple-media-assetBrod
CreateEmptyAsset does not exist anymore. Create or CreateAsync is the way to create an empty asset now. Thanks anyway!Pepita
I think CreateAsync may need a unique id? Based on the doc here they create a unique identifier for the file. Maybe by setting AssetCreationOptions.None, you're trying to create a file which already exists.Climatology
I have double checked that the container I am trying to access is empty. Thanks.Pepita
Can you please post the stack trace of the exception? Also this and this might be helpful to you.Enlighten
The second link seems useful! I 'll try to create the asset using REST API. ThanksPepita
@GeorgeChond I think you mean the first link.Enlighten
@HadiBrais oops sorry!Pepita
Is your application building for a x64 cpu? I get similar issues with 64bit builds and APIs, try building in "Any CPU" and see if it works.Effable
@Effable I tried it with no luck. Thanks for your comment!Pepita
B
1

I was able to create a working demo using a .NET 4.6.1 and following nuget package:

[HttpPost]
public async Task<string> PostUpload()
{
    var bytes = System.Text.Encoding.UTF8.GetBytes("Test");
    var data = new VideoData { Data = bytes };
    CloudMediaContext _context;
    using (MemoryStream ms = new MemoryStream(data.Data))
    {
        var accountName = "accountName";
        var accountKey = @"primaryaccessKey";
        _context = new CloudMediaContext(accountName, accountKey);
        var asset = await _context.Assets.CreateAsync("myjblobassets", AssetCreationOptions.None, CancellationToken.None);
        //... do something with asset and ms ...
    }
    return "http://my-link";
}

* Note: any bytes is fine here since we are not demonstrating the upload part, we are making sure creating a Media Service Asset works.

  1. Add a HTML form and submit button in the index view pointing to this post action, run it and works!!!

enter image description here

Note: In NAME, enter the name of the new account. A Media Services account name is all lower-case numbers or letters with no spaces, and is 3 - 24 characters in length. https://azure.microsoft.com/en-us/documentation/articles/media-services-dotnet-get-started/

Boggle answered 28/7, 2016 at 15:5 Comment(0)
U
0

I can't tell you why it's not working exactly. But i think the problem is more likely to find in the construction of your function than in the Azure API.

It should be done like this:

    // Important is the return value of the function: 
    public async Task<IAsset> CreateAssetBlobAsync(CancellationToken token)
    {            
        var asset = await _context.Assets.CreateAsync("blobContainerName", AssetCreationOptions.None, CancellationToken.None); // or your 'token'

        return asset;
    }

And this async function should be called like this:

public static async Task ProcessNewBlobAsync()
{
     // ...

     var asset = await CreateAssetBlobAsync(token);

     // ...
}

I think the problem will be your using Statement.

Fact is the Read/Write Async methods of MemoryStream don't actually provide any kind of true asynchronous implementation. Which means they always will be synchronously.

(But you didn't call any of them. I fear maybe it will be a synchronous wrapper for the async method. And so it will call the API synchronous instead of asynchronous. This is only a thought.)

I recommend to avoid using-Statements with async for preventing a deadlock.


Utu answered 2/6, 2015 at 11:55 Comment(5)
I don't see any difference with my code, but I tried it and doesn't work! Thanks for your answer though.Pepita
Whahaha! I want to get my head around this. Can you post some more code?Utu
Done. There is nothing more to show unfortunately as the exception is thrown when CreateAsync is called. Thanks for your help!Pepita
@GeorgeChond: After the Update, please check if the code works outside the using Statement.Utu
It make no sense. Why would he call a async method synchronously. Something must wrap the whole thing. Is there anything which could lead to this behavior?Utu
C
0

I hope that "blobContainerName" is a mock you wrote when posting the question? Capital letters and blob containers do not mix :)

Corrective answered 13/1, 2016 at 8:50 Comment(2)
It is working when I am using a plain console application so I don't think the problem is the name of the blob container. Thanks!Pepita
That's interesting, it always causes exceptions for me if I have a capital letter in the container name.Corrective
H
0

Can you try to call method

Task<IAsset> CreateAsync(string assetName, string storageAccountName, AssetCreationOptions options, CancellationToken cancellationToken)

and pass your default storage account name associated with media service. In your example you are calling method which querying default storage account name. Querying of storage account has been done not asynchronously and it is a bug. This storage account query happens once per context not to generate load on server. It will be good if you can query during app initialization and use it within your create asset calls later.

Headward answered 20/1, 2016 at 0:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.