Async and Await didn't work on web api - asp.net-web-api

I am trying to use await on my async method but it didn't work. I input 2 array of parameters when calling the post method, only the last one is inserted
to database(I use Elasticsearch as database so when the _id is the same the document will replaced by the new one). and I found out when insert is not done yet the program is already run to query the database and the result is 0 so it's insert again instead of update.
I already add await on my program but it didn't work out. Can anyone help me with this problem? Thanks
here is my code
// POST api/values
[HttpPost]
public async Task<AvatarModel.AvatarResponse> Post(MultiLanguageTemp[] LangTemp)
{
//process param to multilanguage model
AvatarModel.AvatarResponse Resp = new AvatarModel.AvatarResponse();
try
{
for (int i = 0; i < LangTemp.Length; i++)
{
string Type = LangTemp[i].Type;
if ("ErrorCode".Equals(Type))
{
}
else
{
string GetLabelId = LangTemp[i].LabelId;
string GetTranslation = LangTemp[i].Translation;
MultiLanguage Lang = new MultiLanguage();
Lang.Type = LangTemp[i].Type;
Lang.Site = LangTemp[i].Site;
Lang.Language = LangTemp[i].LangId;
Lang.Source = LangTemp[i].Source;
Lang.TranslationList = new Dictionary<string, string>();
Lang.TranslationList.Clear();
Lang.TranslationList.Add(GetLabelId, GetTranslation);
//search elasticsearch first using id TYPE+SITE+LANG_ID+SOURCE
string ESResponse = await GetMultiLangAsync(Lang);
JObject GetResp = JObject.Parse(ESResponse);
//get elasticsearch Hits count
JToken GetHitsTotal = GetResp.SelectToken("hits.total");
int Hits = int.Parse(GetHitsTotal.ToString());
// if id exist then do update else do insert
if (Hits > 0)
{
string ResponseUpdate = await UpdateMultiLangAsync(GetLabelId, GetTranslation,Lang);
if (!ResponseUpdate.ToString().ToUpper().Contains("ERROR"))
{
Resp.Result = "0000000";
Resp.Message = "Update MultiLanguage Info is Success";
}
else
{
Resp.Result = "9000003";
Resp.Message = "Update MultiLanguage Info into ES failed";
}
}
else
{
//tasks.Add(InsertMultiLangAsync(Lang));
//insert new document into elasticsearch
string InsertESResponse = await InsertMultiLangAsync(Lang);
if (!InsertESResponse.ToUpper().Contains("ERROR"))
{
Resp.Result = "0000000";
Resp.Message = "Insert MultiLanguage Info is Success";
}
else
{
Resp.Result = "9000003";
Resp.Message = "Insert MultiLanguage Info into ES failed";
}
}
}
}
}
catch (Exception E)
{
Resp.Result = "9000005";
Resp.Message = E.Message.ToString();
}
return Resp;
}
public async Task<string> GetMultiLangAsync(MultiLanguage Lang)
{
var Client = new HttpClient();
Client.BaseAddress = new Uri("http://localhost:9200/multilanguage/MultiLangInfo/");
Client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var Query = "{\"query\": {\"match\": {\"_id\":\"" + Lang.Type + Lang.Site + Lang.Language + Lang.Source + "\"}}}";
var StringContent = new StringContent(Query, Encoding.UTF8, "application/json");
var Response = Client.PostAsync("_search", StringContent).Result.Content.ReadAsStringAsync();
//JObject GetResp = JObject.Parse(Response.Result);
return await Response;
}
public async Task<string> InsertMultiLangAsync(MultiLanguage Lang)
{
var Client = new HttpClient();
Client.BaseAddress = new Uri("http://localhost:9200/multilanguage/");
Client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var JsonTextMultiLang = JsonConvert.SerializeObject(Lang, Formatting.None, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
var StringContent = new StringContent(JsonTextMultiLang, Encoding.UTF8, "application/json");
var ResponseInsert = Client.PostAsync("MultiLangInfo/" + Lang.Type + Lang.Site + Lang.Language + Lang.Source, StringContent).Result.Content.ReadAsStringAsync();
return await ResponseInsert;
}
public async Task<string> UpdateMultiLangAsync(string GetLabelId,string GetTranslation, MultiLanguage Lang)
{
var UpdateES = "{\"doc\":{\"TranslationList\":{\"" + GetLabelId + "\":\"" + GetTranslation + "\"}},\"detect_noop\":true}";
var Client = new HttpClient();
Client.BaseAddress = new Uri("http://localhost:9200/multilanguage/MultiLangInfo/" + Lang.Type + Lang.Site + Lang.Language + Lang.Source + "/");
Client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var StringContent = new StringContent(UpdateES, Encoding.UTF8, "application/json");
var ResponseUpdate = Client.PostAsync("_update", StringContent).Result.Content.ReadAsStringAsync();
return await ResponseUpdate;
}

Here is my insight.
Based on the details you provided, you have a long-running task that would eventually create/update a record in your database. Now, the problem you encounter is the second http request you send is not waiting for the first one to finish even though you are using async/await pattern. Well, this is not how it works. Regardless of what you do, whether you block the thread or not, there is thread pooling and distinct http calls will have their own thread. So using async/await would not affect that at all. For the most part, you're using async/await correctly. However, what you're trying to achieve is not done by async/await. You might want to try a messaging system to queue all the requests. In that case, you can't put clients on hold, You'd generate a request id and send it to them immediately and process the request asynchronously in time.

Related

How to update data through Api in Xamarin

I do the update command through the API. Everything seems fine. However, the data is not up to date. When I debug there is no error.
public async Task UpdateViewRatingStore(bool value)
{
var url = baseUrl + userget;
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", mytokenlogin);
string jsonStr = await client.GetStringAsync(url);
var res = JsonConvert.DeserializeObject<Customer>(jsonStr);
var checkunredrating = res.RatingStores;
if (checkunredrating != null)
{
foreach (var r in checkunredrating)
{
r.ID = r.ID;
r.StoreID = r.StoreID;
r.RatingStores = r.RatingStores;
r.CommentStore = r.CommentStore;
r.UserRating = r.UserRating;
r.CreateDay = r.CreateDay;
r.Display = r.Display;
r.ViewStorer = value;
var urlput = baseUrlStoreRating + r.ID;
var stringContent = new StringContent(JsonConvert.SerializeObject(res.RatingStores), Encoding.UTF8, "application/json");
await client.PutAsync(urlput, stringContent);
}
}
}
However when I check in the database it is still not updated. I tested it manually on swagger and Posman was fine. Where did I go wrong? Ask for help. Thank you
you are trying to update a single object, but passing the entire collection every time
instead, try this
foreach (var r in checkunredrating)
{
// you only need to update the changed values
r.ViewStorer = value;
var urlput = baseUrlStoreRating + r.ID;
// only send the current object you are updating
var stringContent = new StringContent(JsonConvert.SerializeObject(r), Encoding.UTF8, "application/json");
await client.PutAsync(urlput, stringContent);
}

How do I Attach an AdaptiveCardFromJson in a LUIS Bot C#?

I asked a similar question recently but wasn't specific enough. I see that there is some code with the AdaptiveCards NuGet Package to attach an AdaptiveCardFromJson and AdaptiveCardFromSDK, which under a the normal Microsoft Bot Model is available.
However, under the Microsoft LUIS Bot Model isn't an option, here's the code I have which returns an employee lookup result from a SQL DB Search:
[LuisIntent("Who_is_Employee")]
public async Task Who_is_EmployeeIntent(IDialogContext context, LuisResult result)
{
EntityRecommendation recommendation;
if (result.TryFindEntity("Communication.ContactName", out recommendation))
{
List<Employee> results = EmployeeService.FindEmployees(recommendation.Entity);
if (results.Count > 0)
{
string response = "";
foreach (Employee e in results)
{
string name = e.FullName;
string title = e.JobTitle;
response += " " + name + " " + title + "\n";
}
await context.PostAsync(response);
}
}
else
{
await context.PostAsync(" Sorry, I couldn't find who you were looking for.");
}
}
I would like that information to be returned as an AdaptiveCard, how do I achieve this?
Mark,
you need to craft your adaptive card either as json or using the SDK to create an instance of AdaptiveCard. Here is a great place to learn more about this.
Once you've crafted your card and have an instance of the AdaptiveCard class, you need to create a new message and attach the card to that message. The new message is what you'll post back to the user.
The code will look something like this
var card = AdaptiveCard.FromJson(<your json here>);
Attachment attachment = new Attachment()
{
ContentType = AdaptiveCard.ContentType,
Content = card
};
var myRespsonse = context.MakeMessage();
myRespsonse.Attachments.Add(attachment);
await context.PostAsync(myRespsonse, CancellationToken.None);
This was the code I ended up having to use to make this successful:
[LuisIntent("Who_is_Employee")]
public async Task Who_is_EmployeeIntent(IDialogContext context, LuisResult result)
{
EntityRecommendation recommendation;
if (result.TryFindEntity("Communication.ContactName", out recommendation))
{
List<Employee> results = EmployeeService.FindEmployees(recommendation.Entity);
if (results.Count > 0)
{
/* Single line per result */
/*
string response = "";
foreach (Employee e in results)
{
string name = e.FullName;
string title = e.JobTitle;
response += " " + name + " " + title + "\n";
}
await context.PostAsync(response);
*/
/* Adaptive card per result */
// Load json template
string physicalPath = System.Web.HttpContext.Current.Server.MapPath("../AdaptiveCards/EmployeeLookup.json");
string jsonTemplate = "";
using (StreamReader r = new StreamReader(physicalPath))
{
jsonTemplate = r.ReadToEnd();
}
var respsonse = context.MakeMessage();
foreach (Employee e in results)
{
string employeeJson = jsonTemplate;
employeeJson = employeeJson.Replace("{{FullName}}", e.FullName);
employeeJson = employeeJson.Replace("{{JobTitle}}", e.JobTitle);
employeeJson = employeeJson.Replace("{{Reference}}", e.Reference);
employeeJson = employeeJson.Replace("{{Phone}}", e.Phone);
employeeJson = employeeJson.Replace("{{Email}}", e.Email);
employeeJson = employeeJson.Replace("{{Mobile}}", e.Mobile);
AdaptiveCard card = AdaptiveCard.FromJson(employeeJson).Card;
Attachment attachment = new Attachment()
{
ContentType = AdaptiveCard.ContentType,
Content = card
};
respsonse.Attachments.Add(attachment);
}
await context.PostAsync(respsonse);
}
}
else
{
await context.PostAsync(" Sorry, I couldn't find who you were looking for.");
}
}

Insert data from server to SQLite Database

I am inserting 3000 plus data from my server to my SQLite Database. The problem is the inserting process is very slow. Is there a better way to insert the data efficiently and effectively? What I am doing is I converted the data I got from my server to JSON Object and insert it one-by-one. I know what I am doing is inefficient. How can I fix this?
public class AndroidSQLiteDb : ISQLiteDB
{
public SQLiteAsyncConnection GetConnection()
{
var dbFileName = "backend.db3";
var documentsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
var path = Path.Combine(documentsPath, dbFileName);
return new SQLiteAsyncConnection(path);
}
}
public async void FirstSyncContacts(string host, string database, string contact)
{
try
{
var db = DependencyService.Get<ISQLiteDB>();
var conn = db.GetConnection();
var sql = "SELECT * FROM tblContacts WHERE Coordinator = '" + contact + "'";
var getContacts = conn.QueryAsync<ContactsTable>(sql);
var resultCount = getContacts.Result.Count;
var current_datetime = DateTime.Now.ToString("yyyy-MM-dd hh:mm:00");
//Check if the retailer has been sync
if (resultCount < 1)
{
try
{
syncStatus.Text = "Syncing Retailer";
var link = Constants.requestUrl + "Host=" + host + "&Database=" + database + "&Contact=" + contact + "&Request=9DpndD";
string contentType = "application/json";
JObject json = new JObject
{
{ "ContactID", contact }
};
HttpClient client = new HttpClient();
var response = await client.PostAsync(link, new StringContent(json.ToString(), Encoding.UTF8, contentType));
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
if (content != "")
{
var contactsresult = JsonConvert.DeserializeObject<List<ContactsData>>(content);
foreach (var item in contactsresult)
{
// update only the properties that you have to...
item.LastSync = Convert.ToDateTime(current_datetime);
item.ServerUpdate = Convert.ToDateTime(item.ServerUpdate);
item.MobileUpdate = Convert.ToDateTime(item.MobileUpdate);
}
await conn.InsertAsync(contactsresult);
}
}
//Proceed to next function
FirstSyncRetailerGroup(host, database, contact);
}
catch (Exception ex)
{
Console.Write("Syncing Retailer Error " + ex.Message);
}
}
//If not get the retailer
else
{
SyncContacts(host, database, contact);
}
}
catch (Exception ex)
{
Console.Write("Syncing Retailer Error " + ex.Message);
}
}
Use the non-async Insert in one background thread, instead of 3000 separate async calls...
Re-use the List from your DeserializeObject step instead of creating new local objects that will just be thrown away on each loop iteration.
No need to assign all those json properties (item.XXX) to another local variable, just update the properties of each existing ContactsData as needed before inserting it into the DB.
Example using SQLiteConnection:
// Use the non-async version of SQLiteConnection
var conn = new SQLiteConnection(dbPath, true, null);
// code removed for example...
await System.Threading.Tasks.Task.Run(() =>
{
var contactsresult = JsonConvert.DeserializeObject<List<ContactsData>>(content);
// start a transaction block so all 3000 records are committed at once.
conn.BeginTransaction();
// Use `foreach` in order shortcut the need to retrieve the object from the list via its index
foreach (var item in contactsresult)
{
// update only the properties that you have to...
item.LastSync = Convert.ToDateTime(current_datetime);
item.ServerUpdate = Convert.ToDateTime(item.ServerUpdate);
item.MobileUpdate = Convert.ToDateTime(item.MobileUpdate);
conn.Insert(item);
}
conn.Commit();
});
Example using SQLiteAsyncConnection:
var db = DependencyService.Get<ISQLiteDB>();
var conn = db.GetConnection();
~~~
var contactsresult = JsonConvert.DeserializeObject<List<ContactsData>>(content);
foreach (var item in contactsresult)
{
// update only the properties that you have to...
item.LastSync = Convert.ToDateTime(current_datetime);
item.ServerUpdate = Convert.ToDateTime(item.ServerUpdate);
item.MobileUpdate = Convert.ToDateTime(item.MobileUpdate);
}
conn.InsertAsync(contactsresult); // Insert the entire list at once...
I had the same problem so even the answer is some years late, maybe can be usefull for somebody.
This is how I did.
First: I get the data all from server as json
var response = await client.GetAsync("your_server_url");
var content = await response.Content.ReadAsStringAsync();
ResponseData = JsonConvert.DeserializeObject<DataModel>(content);
Second: Save data to database
await conn.InsertAllAsync(ResponseData)
But in my case, because our app works offline data, I first insert all data in a temp table, then I get all new records comparing main table with temp table.
NewDataFromTemp = await conn.QueryAsync<DataModel>("SELECT * FROM [TableTemp] t WHERE t.[TABLE_ID] NOT IN (SELECT g.[TABLE_ID] FROM [MainTable] g)");
And insert new records in Main table
await conn.InsertAllAsync(NewDataFromTemp)
Then I check for updated records
UpdatedDataFromTemp = await conn.QueryAsync<DataModel>("SELECT t.* FROM [TableTemp] t, [MainTable] o WHERE t.[TABLE_ID]=o.[TABLE_ID] AND t.[TABLE_UPDATED]>o.[TABLE_UPDATED]");
And update all record in main table
await conn.UpdateAllAsync(UpdatedDataFromTemp);
I use logical delete so when updating the logical delete will be updated too.

How do you create a client using websocket-sharp?

I'm using ClientWebSocket to subscribe to a REST service but want to be able to use websocket-sharp instead.
static async void MonitorISY(string IPAddress, string userName, string password, IMessageWriter writer)
{
ClientWebSocket client = new ClientWebSocket();
client.Options.AddSubProtocol("ISYSUB");
client.Options.SetRequestHeader("Origin", "com.universal-devices.websockets.isy");
var auth = Convert.ToBase64String(Encoding.Default.GetBytes(userName + ":" + password));
client.Options.SetRequestHeader("Authorization", "Basic " + auth);
await client.ConnectAsync(new Uri("ws://" + IPAddress + "/rest/subscribe"), CancellationToken.None);
var receiveBufferSize = 512;
byte[] buffer = new byte[receiveBufferSize];
writer.Clear();
while (true)
{
var result = await client.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
var resultJson = (new UTF8Encoding()).GetString(buffer);
writer.WriteLn(resultJson);
writer.WriteLn();
}
}
Here is my websocket-sharp attempt. When ws.Connect(); is called, I'm getting a Not a WebSocket handshake response error message. In the working code, I have to setup the Origin, SubProtocol and the RequestHeader. I think I'm doing that correctly for the websocket-sharp code, with the exception of the Request Header. I've been unable to find a working example that specifies authentication.
using (var nf = new Notifier())
using (var ws = new WebSocket("ws://172.16.0.40/rest/subscribe", "ISYSUB"))
{
ws.Log.Level = LogLevel.Trace;
var username = "user";
var password = "pass";
ws.Origin = "com.universal-devices.websockets.isy";
ws.SetCredentials(username, password, true);
ws.OnOpen += (sender, e) => ws.Send("Hi, there!");
ws.OnMessage += (sender, e) =>
nf.Notify(
new NotificationMessage
{
Summary = "WebSocket Message",
Body = !e.IsPing ? e.Data : "Received a ping.",
Icon = "notification-message-im"
}
);
ws.OnError += (sender, e) =>
nf.Notify(
new NotificationMessage
{
Summary = "WebSocket Error",
Body = e.Message,
Icon = "notification-message-im"
}
);
ws.OnClose += (sender, e) =>
nf.Notify(
new NotificationMessage
{
Summary = String.Format("WebSocket Close ({0})", e.Code),
Body = e.Reason,
Icon = "notification-message-im"
}
);
ws.Connect();
I think the best example is https://github.com/sta/websocket-sharp/tree/master/Example3
Although I did have to make a few tiny adjustments to get it to compile in Visual Studio 2017 Enterprise.
The index.html is based on http://www.websocket.org/echo.html

WebAPI HttpContext is Null Inside ContinueWith() => tast

I'm just wondering if someone could explain what is happening here.
Given this Post method on an API controller:
public HttpResponseMessage PostImage()
{
var request = HttpContext.Current.Request;
var c = SynchronizationContext.Current;
var result = new HttpResponseMessage(HttpStatusCode.OK);
if (Request.Content.IsMimeMultipartContent())
{
Request.Content.ReadAsMultipartAsync(new MultipartMemoryStreamProvider()).ContinueWith((task) =>
{
MultipartMemoryStreamProvider provider = task.Result;
foreach (HttpContent content in provider.Contents)
{
Stream stream = content.ReadAsStreamAsync().Result;
Image image = Image.FromStream(stream);
var uploadFileName = content.Headers.ContentDisposition.FileName;
var requestInside = HttpContext.Current.Request; // this is always null
string filePath = Path.Combine(HostingEnvironment.MapPath(ConfigurationManager.AppSettings["UserFilesRootDir"]), userprofile.UserCode);
//string[] headerValues = (string[])Request.Headers.GetValues("UniqueId");
string fileName = userprofile.UserCode + ".jpg";
string fullPath = Path.Combine(filePath, fileName);
image.Save(fullPath);
}
});
return result;
}
}
Why would var requestInside = HttpContext.Current.Request; be null?
I've checked all the relevant settings:
<compilation debug="true" targetFramework="4.5">
...
<httpRuntime targetFramework="4.5"
And SynchronizationContext.Current is the newer AspNetSynchronizationContext rather than LegacyAspNetSynchronizationContext.
I'm presuming at the moment that it's because I'm on a different thread, is this a correct assumption?
ContinueWith is not guaranteed to run on the same thread hence the synchronization context could be lost. You could change your call to specify to resume on the current thread with parameter TaskScheduler.Current. See this previous SO answer.
If you use await/async pattern it will automatically capture the current syncronization context on resume once an awaitable operation completes. This is done by resuming the operation on the same thread which is bound to that context. An added benefit, IMHO, is cleaner looking code.
You can change your code to this which uses that pattern. I have not made any other changes to it other than use async/await.
public async Task<HttpResponseMessage> PostImage()
{
var request = HttpContext.Current.Request;
var c = SynchronizationContext.Current;
var result = new HttpResponseMessage(HttpStatusCode.OK);
if (Request.Content.IsMimeMultipartContent())
{
MultipartMemoryStreamProvider provider = await Request.Content.ReadAsMultipartAsync(new MultipartMemoryStreamProvider());
foreach (HttpContent content in provider.Contents)
{
Stream stream = await content.ReadAsStreamAsync();
Image image = Image.FromStream(stream);
var uploadFileName = content.Headers.ContentDisposition.FileName;
var requestInside = HttpContext.Current.Request; // this is always null
string filePath = Path.Combine(HostingEnvironment.MapPath(ConfigurationManager.AppSettings["UserFilesRootDir"]), userprofile.UserCode);
//string[] headerValues = (string[])Request.Headers.GetValues("UniqueId");
string fileName = userprofile.UserCode + ".jpg";
string fullPath = Path.Combine(filePath, fileName);
image.Save(fullPath);
}
}
return result;
}

Resources