I have a function in a c# console application, which calls an async function (My aim is to call an azure function via a console application. This function is responsible for converting zip files to csv files and of course I would like to process the zip file in parallel)
Here you can find a section of my code:
Parallel.ForEach(blobs, (currentblob) =>
{
string FileUrl = currentblob.Uri.ToString();
string FileName = currentblob.Uri.Segments.Last();
//string content = "{ \"fileUri\": \""+ currentblob.Uri.ToString()+ "\" , \"fileName\": \""+ currentblob.Uri.Segments.Last()+"\"}";
Console.WriteLine(currentblob.Uri + " #### " + currentblob.Uri.Segments.Last());
var values = new Dictionary<string, string>
{
{ "fileUri", currentblob.Uri.ToString() },
{ "fileName", currentblob.Uri.Segments.Last() }
};
var content = new FormUrlEncodedContent(values);
string baseURL = #"https://<afu>.azurewebsites.net/api/process_zip_files_by_http_trigger?code=45"; ;
//string urlToInvoke = string.Format("{0}&name={1}", baseURL, FileUrl, FileName);
Run(baseURL, content);
});
And I have an async function:
public static async void Run(string i_URL,FormUrlEncodedContent content)
{
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.PostAsync(i_URL,content);
string responseString = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseString);
Console.ReadLine();
}
I can run my code without any errors, but it doesn't process zip files. But after I stop debugging then I it starts to process the files!!!
I think something is wrong with my async function. Do you have any idea why my code doesn’t work correctly? Do I call my async function correctly?
First, your calling an async void method, these methods provide no way of signalling completion and are only intended for event handlers. Second, to process async methods in parallel you use Task.WhenAll with a Select instead of Parallel.ForEach. Parallel.ForEach is only intended for CPU bound operations and not asynchronous tasks. Finally, you should then use async all the way up to the root of your application and await all results, this will keep your process alive until all your blobs have been handled.
class Program
{
private static HttpClient client = new HttpClient();
static async Task Main(string[] args)
{
var blobs = new List<Blob>();
await ProcessBlobs(blobs);
Console.ReadLine();
}
public static async Task ProcessBlobs(IEnumerable<Blob> blobs)
{
var tasks = blobs.Select(currentblob =>
{
string FileUrl = currentblob.Uri.ToString();
string FileName = currentblob.Uri.Segments.Last();
//string content = "{ \"fileUri\": \""+ currentblob.Uri.ToString()+ "\" , \"fileName\": \""+ currentblob.Uri.Segments.Last()+"\"}";
Console.WriteLine(currentblob.Uri + " #### " + currentblob.Uri.Segments.Last());
var values = new Dictionary<string, string>
{
{ "fileUri", currentblob.Uri.ToString() },
{ "fileName", currentblob.Uri.Segments.Last() }
};
var content = new FormUrlEncodedContent(values);
string baseURL = #"https://<afu>.azurewebsites.net/api/process_zip_files_by_http_trigger?code=45"; ;
//string urlToInvoke = string.Format("{0}&name={1}", baseURL, FileUrl, FileName);
return RunAsync(baseURL, content);
});
await Task.WhenAll(tasks);
}
public static async Task RunAsync(string i_URL, FormUrlEncodedContent content)
{
var response = await client.PostAsync(i_URL, content);
var responseString = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseString);
}
}
Related
I'm trying after "converted/optimized" a C# Unity Project to my Visual Studio in C# to work for me (the code)...
i used this github lib for nft.storage
https://github.com/filipepolizel/unity-nft-storage/blob/main/NFTStorageClient.cs
If i call then with:
string pathtofile = #"" + AppDomain.CurrentDomain.BaseDirectory + "testfile.txt";
await NFTStorageTest.Upload(NFTStorageTest.nftStorageApiUrl, pathtofile);
i get this error:
System.Net.Http.HttpRequestException: Response status code does not indicate success: 404 (Not Found). at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode() at Main.net.NFTStorageTest.Upload(String uri, String pathFile) in C:\...\NFTStorageTest.cs:line 173
Here is my Code:
// nft.storage API endpoint
internal static readonly string nftStorageApiUrl = "https://api.nft.storage/";
// HTTP client to communicate with nft.storage
internal static readonly HttpClient nftClient = new HttpClient();
// http client to communicate with IPFS API
internal static readonly HttpClient ipfsClient = new HttpClient();
// nft.storage API key
public string apiToken = "XXX";
public void Start()
{
nftClient.DefaultRequestHeaders.Add("Accept", "application/json");
if (apiToken != null)
{
nftClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiToken);
}
else
{
// log in console in case no API key is found during initialization
Console.WriteLine("Starting NFT Storage Client without API key, please call 'SetApiToken' method before using class methods.");
}
}
public async Task<NFTStorageUploadResponse> UploadDataFromFile(string path)
{
StreamReader reader = new StreamReader(path);
string data = reader.ReadToEnd();
reader.Close();
Console.WriteLine("Uploading...");
return await UploadDataFromString(data);
}
public async Task<NFTStorageUploadResponse> UploadDataFromString(string data)
{
string requestUri = nftStorageApiUrl + "/upload";
string rawResponse = await Upload(requestUri, data);
string jsonString = JsonConvert.SerializeObject(rawResponse);
NFTStorageUploadResponse parsedResponse = JsonConvert.DeserializeObject<NFTStorageUploadResponse>(jsonString);
return parsedResponse;
}
public static async Task<string> Upload(string uri, string pathFile)
{
byte[] bytes = File.ReadAllBytes(pathFile);
using (var content = new ByteArrayContent(bytes))
{
content.Headers.ContentType = new MediaTypeHeaderValue("*/*");
//Send it
var response = await nftClient.PostAsync(uri, content);
response.EnsureSuccessStatusCode();
Stream responseStream = await response.Content.ReadAsStreamAsync();
StreamReader reader = new StreamReader(responseStream);
return reader.ReadToEnd();
}
}`
whats my mistake and how can i fix it?
await NFTStorageTest.Upload(NFTStorageTest.nftStorageApiUrl, pathtofile);
This should be called with
$”{NFTStorageTest.nftStorageApiUrl}upload”
See the original file
https://github.com/filipepolizel/unity-nft-storage/blob/45bedb5c421982645bc9bf49d12beed8f2cdb9d3/NFTStorageClient.cs#L297
I'm currently working on a project which consists of 2 application, one that's basically a rest server and a client. On the server part all seems to work fine but I have a deadlock when posting async in the client application.
The post handler in the server application is as for now (using Nancy):
Post("/", args =>
{
bool updated = false;
string json = Request.Body.AsString();
PostData data = JsonConvert.DeserializeObject<PostData>(json);
int clientCode = data.ClientCode;
bool started = data.Started;
// TODO:
// Update database
return updated;
}
);
The deadlocking part of the client application is (I'm also adding the GET handling just for completeness, but it's working fine):
public async Task<string> Get()
{
string actualRequest = $"{uri}/{request}";
response = await client.GetAsync(actualRequest);
response.EnsureSuccessStatusCode();
result = await response.Content.ReadAsStringAsync();
return result;
}
public async Task<string> Post(object data)
{
string json = JsonConvert.SerializeObject(data);
StringContent content = new StringContent(json, Encoding.UTF8, "application/json");
response = await client.PostAsync(uri, content);
result = await response.Content.ReadAsStringAsync();
return result;
}
The small chunk of code used for testing is:
private static string response = "";
private static async Task TestPostConnection(RestClient client)
{
PostData data = new PostData(1, true);
response = await client.Post(data);
}
private static async Task TestGetConnection(RestClient client)
{
client.Request = "id=1";
response = await client.Get();
}
private static async Task InitializeClient()
{
string ipAddress = "localhost";
RestClient client = new RestClient("RestClient", new Uri($"http://{ipAddress}:{8080}"));
await TestGetConnection(client); // OK
await TestPostConnection(client);
}
Probably it's something stupid that I can't see because it's like 4 hours that I'm working on it :P
Thank in advantage!
Edit:
I'm using .net framework (4.7.2) and I'm not calling Result anywhere (just searched in Visual Studio for double check)
This is the Main method:
[STAThread]
static async Task Main()
{
await InitializeClient(); // Call the async methods above
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new ClientForm(response))
}
I just solved the it.
The problem was the return type of the post handler in the server application
Post("/", args =>
{
bool updated = false;
string json = Request.Body.AsString();
PostData data = JsonConvert.DeserializeObject<PostData>(json);
int clientCode = data.ClientCode;
bool started = data.Started;
// TODO:
// Update database
return updated.ToString();
}
);
The RestClient.Post() that I've implemented was expecting a string as a result (in fact the type of the method si (async) Task<string> but I was returning a bool).
Adding ToString() solved it.
Thanks all!
I design a login form in xamarin Form PCL and I want to call a my webservice that will return JSON. For this, I created two functions for the same but both are n is not returning values.
Can you please tell me what I am doing wrong?
async void OnLoginButtonClick(object sender, EventArgs e)
{
if (usernameEntry.Text != "" && passwordEntry.Text != "")
{
var response = GetLoginDetails(usernameEntry.Text,passwordEntry.Text);
var getData = await getDataFromService(usernameEntry.Text, passwordEntry.Text);
}
}
public static async Task<HttpResponseMessage> GetLoginDetails(string username, string password)
{
try
{
var httpClient = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "http://mywebserverIP/api/Users?Username=" + username + "&Password=" + password);
var response = await httpClient.SendAsync(request);
return response;
}
catch (Exception ex)
{
throw;
}
}
public static async Task<dynamic> getDataFromService(string username, string password)
{
using (var client = new HttpClient())
{
var responseText = await client.GetStringAsync("http://mywebserverIP/api/Users?Username=" + username + "&Password=" + password);
dynamic data = JsonConvert.DeserializeObject(responseText);
return data;
}
}
Thanks for your comment in Advance.
As you not awaiting the first method , request thread will not wait till it returns the value so , 1st change you have to make is to
var response = await GetLoginDetails()
For the second method
var getData = await getDataFromService()
I do not see any issue. I am not sure how you know that this method is not returning any values. Better to log the response of the both the method call and check.
Use await.
First in the
var response = await GetLoginDetails(...
then maybe in the deserializeobject method too (this one i'm not sure)
dynamic data = await Task.Run(() => JsonConvert.DeserializeObject(responseText));
After running with the debugger this line of code
HttpClient client = new HttpClient()
the application suddenly exits. Anybody have some ideas why this appears?
This is the code:
static void Main(string[] args)
{
Task.Run(() => translate("en", "bg", "hello").Wait());
}
static async Task translate(string sourceLang, string targetLang, string sourceText)
{
string page = "http://www.transltr.org/api/translate?text=" +
sourceText + "&to=" + targetLang + "&from=" + sourceLang;
try
{
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(page);
response.EnsureSuccessStatusCode();
if (response.IsSuccessStatusCode)
{
string trans = await response.Content.ReadAsStringAsync();
Console.WriteLine(trans);
Console.ReadLine();
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
and it stops executing after this line
HttpClient client = new HttpClient()
Task.Run(() => translate("en", "bg", "hello")).Wait();
Code is working, with a small adjustment you need to await task created at Task.Run
I have the following method, on a windows-store project, to upload a file
public async Task<Boolean> UploadFileStreamService(Stream binaries, String fileName, String filePath)
{
try
{
filePath = Uri.EscapeDataString(filePath);
using (var httpClient = new HttpClient { BaseAddress = Constants.baseAddress })
{
var content = new StreamContent(binaries);
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", App.Current.Resources["token"] as string);
App.Current.Resources["TaskUpload"] = true;
using (var response = await httpClient.PostAsync("file?fileName=" + filePath, content))
{
string responseData = await response.Content.ReadAsStringAsync();
if (responseData.Contains("errorCode"))
throw new Exception("Exception: " + responseData);
else
{
JsonObject jObj = new JsonObject();
JsonObject.TryParse(responseData, out jObj);
if (jObj.ContainsKey("fileId"))
{
if (jObj["fileId"].ValueType != JsonValueType.Null)
{
App.Current.Resources["NewVersionDoc"] = jObj["fileId"].GetString();
}
}
}
return true;
}
}
}
catch (Exception e)
{
...
}
}
And on the app.xaml.cs i have on the constructor:
NetworkInformation.NetworkStatusChanged +=
NetworkInformation_NetworkStatusChanged; // Listen to connectivity changes
And on that method i check for the connection changes.
What i would like to know is how to stop a upload task when i detect that network change ( from having internet to not having).
You can use cancellation tokens. You need CancellationTokenSource:
private readonly CancellationTokenSource _cts = new CancellationTokenSource();
Then pass token to your UploadFileStreamService method (use _cts.Token to get token):
public async Task<Boolean> UploadFileStreamService(Stream binaries, String fileName, String filePath, CancellationToken ct)
And use another overload of PostAsync which accepts token (note - also use overloads that accept tokens for all other async methods where possible, for example for ReadAsStringAsync):
using (var response = await httpClient.PostAsync("file?fileName=" + filePath, content, ct))
Then when you found network connection is lost, cancel with:
_cts.Cancel();
Note that this will throw OperationCancelledException on PostAsync call, which you may (or may not) want to handle somehow.