I am trying to make an API call in an ASP.NET web form. The request works fine in Console, but async requests are timing out and not working. Please advise.
Console App Code:
using System;
using System.Collections.Generic;
using System.Text;
using Newegg.Marketplace.SDK;
using Newegg.Marketplace.SDK.Base;
using Newegg.Marketplace.SDK.DataFeed;
using Newegg.Marketplace.SDK.DataFeed.Model;
using Newegg.Marketplace.SDK.Item;
using Newegg.Marketplace.SDK.Item.Model;
using Newegg.Marketplace.SDK.Order;
using Newegg.Marketplace.SDK.Order.Model;
using Newegg.Marketplace.SDK.Other;
using Newegg.Marketplace.SDK.Other.Model;
using Newegg.Marketplace.SDK.Report.Model;
using Newegg.Marketplace.SDK.RMA;
using Newegg.Marketplace.SDK.RMA.Model;
using Newegg.Marketplace.SDK.Seller;
using Newegg.Marketplace.SDK.Seller.Model;
using Newegg.Marketplace.SDK.Shipping.Model;
namespace example
{
public class Demo
{
private OrderCall ordercall;
private ItemCall itemCall;
private SellerCall sellerCall;
private DatafeedCall datafeedCall;
private RMACall rmaCall;
private ShippingCall shippingCall;
private ReportCall reportCall;
private OtherCall otherCall;
public Demo()
{
//Construct an APIConfig with SellerID, APIKey(Authorization) and SecretKey.
APIConfig config = new APIConfig("****", "********************************", "********-****-****-****-************");
// or load the config file to get it.
//APIConfig config = APIConfig.FromJsonFile("setting.json");
//Create a APIClient with the config
APIClient client = new APIClient(config);
//Create the Api Call object with he client.
ordercall = new OrderCall(client);
itemCall = new ItemCall(client);
sellerCall = new SellerCall(client);
datafeedCall = new DatafeedCall(client);
rmaCall = new RMACall(client);
shippingCall = new ShippingCall(client);
reportCall = new ReportCall(client);
otherCall = new OtherCall(client);
}
public void GetOrderStatus()
{
Console.WriteLine("GetOrderStatus");
// Send your request and get response
var orderstatus = ordercall.GetOrderStatus("105137040").Result;
// Use the data pre you business
Console.WriteLine(string.Format("There order status is {0}.", orderstatus.OrderStatusName));
}
}
My ASP.NET code
public partial class HomePage : System.Web.UI.Page
{
private OrderCall orderCall;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
RegisterAsyncTask(new PageAsyncTask(LoadSomeData));
}
}
public async Task LoadSomeData()
{
string path = #"C:\Users\*****\source\repos\Orders\Settings\setting.json";
APIConfig config = APIConfig.FromJsonFile(path);
APIClient client = new APIClient(config);
orderCall = new OrderCall(client);
try
{
var orderstatus = await orderCall.GetOrderStatus("105137040");
Debug.Write(orderstatus.OrderStatusName);
Connect.BackColor = Color.Green;
}
catch (Exception ex)
{
Connect.BackColor = Color.Red;
Debug.WriteLine(ex.Message);
}
}
}
}
After using my code, the page freezes and eventually times out. The returned object is also null or returns an error.
Related
Im needing assistance on how to take a user input from a login page, check the Database for the username, and if it exist in the DB, pass it to another file that will pull the information pertaining to that username from the cosmosDB.
Below is the loginPage code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace ToDoItems.Core.Pages
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class LoginPage : ContentPage
{
public LoginPage()
{
InitializeComponent();
}
async void SignInProcedure(object sender, EventArgs e)
{
User user = new User(Entry_Username.Text, Entry_Password.Text);
if (user.CheckInformation())
{
//await Navigation.PushModalAsync(new CosmosDBService(Entry_Username.Text));
await Navigation.PushAsync(new ToDoItemsPage());
}
else
{
DisplayAlert("Login", "Login Failed", "Okay");
}
}
}
}
And Im trying to, first check the cosmosDB for the user name, then pass the username if it exist to the cosmosDBservice file to get the information associated with the username:
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using System;
using System.Data;
using System.Diagnostics;
using Microsoft.Azure.Documents.Linq;
using Xamarin.Forms;
using ToDoItems.Core.Pages;
namespace ToDoItems.Core
{
public class CosmosDBService
{
static string queryname;
public CosmosDBService(string logname)
{
// store the parameter for use later;
queryname = logname;
}
static DocumentClient docClient = null;
static readonly string databaseName = "Tasks";
static readonly string collectionName = "Items";
static async Task<bool> Initialize()
{
if (docClient != null)
return true;
try
{
docClient = new DocumentClient(new Uri(APIKeys.CosmosEndpointUrl), APIKeys.CosmosAuthKey);
// Create the database - this can also be done through the portal
await docClient.CreateDatabaseIfNotExistsAsync(new Database { Id = databaseName });
// Create the collection - make sure to specify the RUs - has pricing implications
// This can also be done through the portal
await docClient.CreateDocumentCollectionIfNotExistsAsync(
UriFactory.CreateDatabaseUri(databaseName),
new DocumentCollection { Id = collectionName },
new RequestOptions { OfferThroughput = 400 }
);
}
catch (Exception ex)
{
Debug.WriteLine(ex);
docClient = null;
return false;
}
return true;
}
// <GetToDoItems>
/// <summary>
/// </summary>
/// <returns></returns>
///
public async static Task<List<ToDoItem>> GetToDoItems()
{
var todos = new List<ToDoItem>();
if (!await Initialize())
return todos;
var todoQuery = docClient.CreateDocumentQuery<ToDoItem>(
UriFactory.CreateDocumentCollectionUri(databaseName, collectionName),
new FeedOptions { MaxItemCount = -1, EnableCrossPartitionQuery = true })
.Where(name => name.Name == queryname)
.AsDocumentQuery();
while (todoQuery.HasMoreResults)
{
var queryResults = await todoQuery.ExecuteNextAsync<ToDoItem>();
todos.AddRange(queryResults);
}
return todos;
}
// </GetToDoItems>
you are already passing the username to the constructor of CosmosDBService
public void CosmosDBService(string user)
{
...
}
you need to create a class variable to store it
string username;
public void CosmosDBService(string user)
{
// store the parameter for use later
username = user;
...
}
then in your query use that class variable
public async static Task<List<ToDoItem>> GetToDoItems()
{
...
.Where(name => name.Name == username)
...
}
I am attempting to retrieve Azure AD user profile data via the Microsoft Graph API. I have set up a small Visual Studio MVC app using code examples from various sources, primarily Microsoft. In my ignorance, I thought this would be a fairly simple process.
I have browsed other similar cases on SO and have attempted to make use of suggestions from others but to no avail. I have been troubleshooting this issue for four days and would greatly appreciate any assistance.
// UserProfileController.cs
-- contains the calling method: var graphToken = await AuthenticationHelper.GetGraphAccessToken();
//
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Security.Claims;
using System.Web;
using System.Web.Mvc;
using System.Threading.Tasks;
using Microsoft.Graph;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OpenIdConnect;
using SSO_Test.Models;
using SSO_Test.Utils;
using System.Net.Http.Headers;
namespace SSO_Test.Controllers
{
[Authorize]
public class UserProfileController : Controller
{
private ApplicationDbContext db = new ApplicationDbContext();
private string clientId = ConfigurationManager.AppSettings["ClientId"];
private string appKey = ConfigurationManager.AppSettings["ClientSecret"];
private string aadInstance = ConfigurationManager.AppSettings["AADInstance"];
private string graphResourceID = "https://graph.microsoft.com";
// GET: UserProfile
public async Task<ActionResult> Index()
{
string tenantID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
string userObjectID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
try
{
var graphToken = await AuthenticationHelper.GetGraphAccessToken();
var authenticationProvider = new DelegateAuthenticationProvider(
(requestMessage) =>
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", graphToken);
return Task.FromResult(0);
});
var graphClient = new GraphServiceClient(authenticationProvider);
var user = await graphClient.Me.Request().GetAsync();
return View(user);
}
catch (AdalException ex)
{
// Return to error page.
ViewBag.Message = ex.Message;
return View("Error");
}
// if the above failed, the user needs to explicitly re-authenticate for the app to obtain the required token
catch (Exception)
{
return View("Relogin");
}
}
public void RefreshSession()
{
HttpContext.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties { RedirectUri = "/Home" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
}
//AuthenticationHelper.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Web;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using SSO_Test.Models;
namespace SSO_Test.Utils
{
public static class AuthenticationHelper
{
public static async Task<string> GetGraphAccessToken()
{
var signInUserId = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
var userObjectId = ClaimsPrincipal.Current.FindFirst(SettingsHelper.ClaimTypeObjectIdentifier).Value;
var clientCredential = new ClientCredential(SettingsHelper.ClientId, SettingsHelper.ClientSecret);
var userIdentifier = new UserIdentifier(userObjectId, UserIdentifierType.UniqueId);
// create auth context
AuthenticationContext authContext = new AuthenticationContext(SettingsHelper.AzureAdAuthority, new ADALTokenCache(signInUserId));
//added check point for verification purposes
System.Diagnostics.Debug.WriteLine("Check point #1");
//GOOD TO THIS POINT
var result = await authContext.AcquireTokenSilentAsync(SettingsHelper.AzureAdGraphResourceURL, clientCredential, userIdentifier);
//ERROR MESSAGE: "Failed to acquire token silently as no token was found in the cache. Call method AcquireToken"
System.Diagnostics.Debug.WriteLine("Check point #2");
//app never reaches the second check point
return result.AccessToken;
}
}
}
//ADALTokenCache.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Security;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
namespace SSO_Test.Models
{
public class ADALTokenCache : TokenCache
{
private ApplicationDbContext db = new ApplicationDbContext();
private string userId;
private UserTokenCache Cache;
public ADALTokenCache(string signedInUserId)
{
// associate the cache to the current user of the web app
userId = signedInUserId;
this.BeforeAccess = BeforeAccessNotification;
this.AfterAccess = AfterAccessNotification;
this.BeforeWrite = BeforeWriteNotification;
// look up the entry in the database
Cache = db.UserTokenCacheList.FirstOrDefault(c => c.webUserUniqueId == userId);
// place the entry in memory
this.DeserializeAdalV3((Cache == null) ? null : MachineKey.Unprotect(Cache.cacheBits, "ADALCache"));
}
// clean up the database
public override void Clear()
{
base.Clear();
var cacheEntry = db.UserTokenCacheList.FirstOrDefault(c => c.webUserUniqueId == userId);
db.UserTokenCacheList.Remove(cacheEntry);
db.SaveChanges();
}
// Notification raised before ADAL accesses the cache.
// This is your chance to update the in-memory copy from the DB, if the in-memory version is stale
void BeforeAccessNotification(TokenCacheNotificationArgs args)
{
if (Cache == null)
{
// first time access
Cache = db.UserTokenCacheList.FirstOrDefault(c => c.webUserUniqueId == userId);
}
else
{
// retrieve last write from the DB
var status = from e in db.UserTokenCacheList
where (e.webUserUniqueId == userId)
select new
{
LastWrite = e.LastWrite
};
// if the in-memory copy is older than the persistent copy
if (status.First().LastWrite > Cache.LastWrite)
{
// read from from storage, update in-memory copy
Cache = db.UserTokenCacheList.FirstOrDefault(c => c.webUserUniqueId == userId);
}
}
this.DeserializeAdalV3((Cache == null) ? null : MachineKey.Unprotect(Cache.cacheBits, "ADALCache"));
}
// Notification raised after ADAL accessed the cache.
// If the HasStateChanged flag is set, ADAL changed the content of the cache
void AfterAccessNotification(TokenCacheNotificationArgs args)
{
// if state changed
if (this.HasStateChanged)
{
Cache = new UserTokenCache
{
webUserUniqueId = userId,
//cacheBits = MachineKey.Protect(this.Serialize(), "ADALCache"),
cacheBits = MachineKey.Protect(this.SerializeAdalV3(), "ADALCache"),
LastWrite = DateTime.Now
};
// update the DB and the lastwrite
db.Entry(Cache).State = Cache.UserTokenCacheId == 0 ? EntityState.Added : EntityState.Modified;
db.SaveChanges();
this.HasStateChanged = false;
}
}
void BeforeWriteNotification(TokenCacheNotificationArgs args)
{
// if you want to ensure that no concurrent write take place, use this notification to place a lock on the entry
}
public override void DeleteItem(TokenCacheItem item)
{
base.DeleteItem(item);
}
}
}
//ApplicationDbContext.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
using System.Linq;
using System.Web;
namespace SSO_Test.Models
{
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
public DbSet<UserTokenCache> UserTokenCacheList { get; set; }
}
public class UserTokenCache
{
[Key]
public int UserTokenCacheId { get; set; }
public string webUserUniqueId { get; set; }
public byte[] cacheBits { get; set; }
public DateTime LastWrite { get; set; }
}
}
As you can see, I have noted in the GetGraphAccessToken() method the error message:
"Failed to acquire token silently as no token was found in the cache.
Call method AcquireToken".
I was able to isolate the AcquireTokenSilentAsync() method as the culprit by bracketing it with a pair of Debug.Writeline statements, the first which ran successfully and the second which did not. This was verified by reviewing the contents of the VS Output window, as follows:
Check point #1
Exception thrown:
'Microsoft.IdentityModel.Clients.ActiveDirectory.AdalSilentTokenAcquisitionException'
in mscorlib.dll
The program '[13980] iisexpress.exe' has exited with code -1
(0xffffffff).
I really want this thing to work and I would much prefer utilizing the Graph SDK approach as opposed to using a REST API.
Again, I have been banging my head against the wall for four-plus days. My head is okay but the wall is in bad shape.
Thanks in advance.
If AcquireTokenSilent fails, it means that there is no token in the cache so you have to go get one via AcquireToken as in this.
You've tagged the question with "B2C" but is looks like you are using Azure AD?
There are a full set of Authentication Providers for the standard set of OAuth flows that are now available so you don't have to use the DelegatedAuthenticationProvider any more. https://github.com/microsoftgraph/msgraph-sdk-dotnet-auth There are docs about how to chose the right Auth provider based on scenario here https://learn.microsoft.com/en-us/graph/sdks/choose-authentication-providers?tabs=CS
We have just upgraded our SyncGatewaty to 2.1. So now I’m refactoring our client code to use CouchbaseLite 2.1. When I try to replicate I get the error:
Got LiteCore error: Not Found (6/404)
I originally got the error when connecting to our Dev Server, and then installed a local clean copy on my laptop and I get the same error when trying to connect to it too.
Log:
INFO) Couchbase 2019-01-10T10:56:47.8503147-07:00 (Startup) [1] CouchbaseLite/2.1.2 (.NET; Microsoft Windows 10.0.17763 ) Build/13 LiteCore/ (15) Commit/9aebf28
WARNING) LiteCore 2019-01-10T10:56:48.1943139-07:00 {C4SocketImpl#1}==> class litecore::repl::C4SocketImpl ws://localhost.com:443//_blipsync
WARNING) LiteCore 2019-01-10T10:56:48.1943139-07:00 {C4SocketImpl#1} Unexpected or unclean socket disconnect! (reason=WebSocket status, code=404)
ERROR) Sync 2019-01-10T10:56:48.1993137-07:00 {Repl#2}==> class litecore::repl::Replicator c:\temp\content_meta_data.cblite2\ ->ws://localhost:443//_blipsync
ERROR) Sync 2019-01-10T10:56:48.1993137-0
7:00 {Repl#2} Got LiteCore error: Not Found (6/404)
My code:
using System;
using System.IO;
using Couchbase.Lite;
using Couchbase.Lite.Support;
using Couchbase.Lite.Sync;
using NLog;
namespace ReplicatorExample
{
public class DatabaseManager
{
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
public const string BUCKET_CONTENT_META_DATA = "content_meta_data";
private static DatabaseManager _instance;
public static DatabaseManager GetInstance()
{
NetDesktop.Activate();
NetDesktop.EnableTextLogging("logs");
return _instance ?? (_instance = new DatabaseManager());
}
public void InitializeBuckets()
{
try
{
var defaultAuthenticator = GetDefaultAuthenticator();
var dirInfo = new DirectoryInfo($"c:\\temp\\{BUCKET_CONTENT_META_DATA}");
if (!dirInfo.Parent.Exists)
{
dirInfo.Parent.Create();
}
var database = new Database(dirInfo.FullName);
// Create replicator to push and pull changes to and from the cloud
var targetEndpoint = new URLEndpoint(new Uri("ws://localhost:4985"));
var replConfig = new ReplicatorConfiguration(database, targetEndpoint)
{
Authenticator = defaultAuthenticator,
Continuous = true,
//Channels = new List<string>
//{
// "approved",
//
//}
};
var replicator = new Replicator(replConfig);
replicator.AddChangeListener((sender, args) =>
{
if (args.Status.Error != null)
{
_log.Error($"{args.Status.Error}");
}
else
{
_log.Debug(args.Status);
}
});
replicator.Start();
}
catch (Exception e)
{
_log.Error(e);
}
}
private Authenticator GetDefaultAuthenticator()
{
return new BasicAuthenticator("BigD","123456");
}
}
}
I believe you need to specify the database name in the URL for targetEndpoint.
E.g: var targetEndpoint = new URLEndpoint(new Uri("ws://localhost:4984/mydatabase"));
I have created a REST Api in ASP.Net web form. I can read and write data from MySql database using the API (I tested the Api using chrome's REST Client extension). Now I am trying to consume the Rest Api in my cross platform xamarin forms. I just added a button in my app and see whether it can retrieve data if I click the button. I inserted a breakpoint at the var Json to see whether I'm getting any data. But I am not able to retrieve it. I am running the Web Api in localhost. I run the app in VS Android emulator. Please guide me on how to properly consume the REST web service. Thank you.
Web Api
namespace WorkAppApi.Controllers
{
public class MachinesController : ApiController
{
// GET: api/Machines
public List<machines> Get()
{
DBConn db = new DBConn();
return db.getMachineList();
}
// GET: api/Machines/5
public machines Get(long id)
{
DBConn db = new DBConn();
machines m = db.getMachine(id);
return m;
}
// POST: api/Machines
public HttpResponseMessage Post([FromBody]machines value)
{
DBConn db = new DBConn();
long id;
id = db.addMachine(value);
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created);
response.Headers.Location = new Uri(Request.RequestUri, String.Format("machines/{0}", id));
return response;
}
// PUT: api/Machines/5
public void Put(int id, [FromBody]string value)
{
}
// DELETE: api/Machines/5
public void Delete(int id)
{
}
}
}
Function submit button.
namespace WorkApp
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class TestPage : ContentPage
{
private string Uri = "http://192.168.0.124:59547/api/Machines/";
public TestPage ()
{
InitializeComponent ();
check();
}
private async Task submit(object sender, EventArgs e)
{
var httpClient = new HttpClient();
var json = await httpClient.GetAsync(Uri);
}
}
}
this would be my answer:
namespace WorkApp
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class TestPage : ContentPage
{
private string Uri = "http://192.168.0.124:59547/api/";
List<Machine> machines;
public TestPage ()
{
InitializeComponent ();
check();
}
private async Task submit(object sender, EventArgs e)
{
var httpClient = new HttpClient();
httpClient.BaseAddress = Uri // I have changed the Uri variabele, you should extend this class and give it the same base address in the constructor.
var resp= await httpClient.GetAsync("Machines");
if (resp.Result.IsSuccessStatusCode)
{
var repStr = resp.Result.Content.ReadAsStringAsync();
machines= JsonConvert.DeserializeObject<List<Machine>>(repStr.Result.ToString());
}
}
}
}
I'm new in C# and I'd like to crate library for my RESTlike API in Windows Phone application.
My api structure is sort of
http://mysiteurl/api/method_name.json
So I would like to call api with (method_name, params) and return specified class.
When I launch this code on my Windows Phone 8.1 program freeze and nothing happens. Also I can see anything on serverside (any call)
API.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Diagnostics;
using System.Threading.Tasks;
using Newtonsoft.Json;
using SampleProg.Model;
namespace SampleProg.Lib
{
public class Api
{
public async Task<VersionClass> versionInfo()
{
Debug.WriteLine("versionInfo()");
var result = await this.Post<VersionClass>("version", "{}", null);
Debug.WriteLine(result);
return result;
}
public async Task<TReturnType> Post<TReturnType>(string methodName, string data, Dictionary<string, string> headers)
{
var webClient = new WebClient();
webClient.Headers["Content-Type"] = "application/json";
webClient.Encoding = Encoding.UTF8;
var uri = new Uri(String.Format("http://mysiteurl/api/{0}.json", methodName));
if (headers != null)
{
foreach (var key in headers.Keys)
{
webClient.Headers[key] = headers[key];
}
}
return await Post<TReturnType>(webClient, uri, data);
}
private async Task<TReturnType> Post<TReturnType>(WebClient webClient, Uri uri, string jsonData)
{
TReturnType returnObject = default(TReturnType);
var taskCompletionSource = new TaskCompletionSource<TReturnType>();
webClient.UploadStringCompleted += (s, e) =>
{
var result = e.Result;
try
{
Debug.WriteLine(result);
returnObject = JsonConvert.DeserializeObject<TReturnType>(result);
taskCompletionSource.SetResult(returnObject);
}
catch (Exception ex)
{
var newEx = new Exception(
string.Format("Failed to deserialize server response: {0}", result), ex);
taskCompletionSource.SetException(newEx);
}
};
webClient.UploadStringAsync(uri, "POST", jsonData);
return await taskCompletionSource.Task;
}
}
}
App.xaml.cs (I call this from public App() {} ):
var api = new Api();
var apiTask = api.versionInfo();
VersionClass version = (VersionClass)apiTask.Result;
Debug.WriteLine(version.num);
VersionClass.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace SampleProg.Model
{
public class VersionClass
{
[JsonProperty("version")]
public int num { get; set; }
}
}
Could you tell me what is wrong? I use this code (at bottom) http://sandor68.rssing.com/chan-11557297/all_p1.html as base
This:
VersionClass version = (VersionClass) apiTask.Result;
Is deadlocking your app.
What's happening is the SynchronizationContext is trying to marshal control back to the UI thread after the await inside your inner method.
You should always await on a Task:
VersionClass version = (VersionClass) await apiTask;
Also, be aware that your wrapping of UploadStringAsync which returns a Task is already made for you OOTB via WebClient.UploadStringTaskAsync
Edit
If you choose to continue your version of Post, you should not await on the returned Task, you should let the user do that, and unregister the handler from the UploadStringCompleted:
private async Task<TReturnType> Post<TReturnType>(WebClient webClient, Uri uri, string jsonData)
{
TReturnType returnObject = default(TReturnType);
var taskCompletionSource = new TaskCompletionSource<TReturnType>();
UploadStringCompletedEventHandler handler = null;
handler = (s, e) =>
{
webClient.UploadStringCompleted -= handler;
var result = e.Result;
try
{
Debug.WriteLine(result);
returnObject = JsonConvert.DeserializeObject<TReturnType>(result);
taskCompletionSource.SetResult(returnObject);
}
catch (Exception ex)
{
var newEx = new Exception(
string.Format("Failed to deserialize server response: {0}", result), ex);
taskCompletionSource.SetException(newEx);
}
};
webClient.UploadStringCompleted += handler;
webClient.UploadStringAsync(uri, "POST", jsonData);
return taskCompletionSource.Task;
}