C# BotFramework FormFlow, pass values to an external api - c#

I am currently developing a simple c# formflow bot that captures the values and sends those values off to an external api, gets the json data back from the external api and creates Card Attachments based on the results returned. I am making the call to the external api in the OnCompletion delegate as follows, To keep it simple I am not passing any values to the api (For testing purposes)
.OnCompletion(async (context, profileForm) =>
{
var reply = context.MakeMessage();
var carsFromApi = await GetCarsAsync("/api/values");
reply.AttachmentLayout = AttachmentLayoutTypes.Carousel;
reply.Attachments = GetCards(carsFromApi);
await context.PostAsync(reply);
// Tell the user that the form is complete
})
I make the call to the api and store the results in "carsFromApi" , I step into that which is the following code snippet
private static async Task<List<Car>> GetCarsAsync(string path)
{
List<Car> car = new List<Car>();
HttpResponseMessage response = await client.GetAsync(path);
if (response.IsSuccessStatusCode)
{
car = await response.Content.ReadAsAsync<List<Car>>();
}
return await response.Content.ReadAsAsync<List<Car>>();
}
Problem is when I press F10 and go to the next line which is "reply.AttachmentLayout = AttachmentLayoutTypes.Carousel;". The local variable that stored the cars "carsFromApi " is now null. This is the part where it all falls over. I cant pass this "carsFromApi" to "reply.Attachments = GetCards(carsFromApi);" I have tried to store the data in a private variable but that also seems to be null. The external api is working because it just returns a list of static text for now. Any ideas? Thanks in advance.

Based on what you are describing it sounds that your code is not existing through the path of the if (response.IsSuccessStatusCode). Check if that point is reached as I suspect an exception or something is going wrong with the request.
Alternatively, you can try doing the request in the ResumeAfter<T> method you specified when calling the Form instead of that in the OnCompletion delegate

Related

HTTPClient await GetByteArrayAsync returns to unexpected code

I'm new to async calls and face an issue I can't find a solution for.
I have a class library I'm calling from a CRM software. The CRM application sends XML data at the beginning and it expects XML to be returned from the library if the screen data should be updated by the library.
In the library I'm calling a function and this functions calls another function and
the third function in that row is an async function with an await get call.
Here is an overview about the structure. When
public string[] Function1(string sXmlDoc)
{
//...
Function2(par1, par2);
//Returns to here from Async Call in Function 3
return xml;
}
public void Function2(string par1, string par2)
{
//...
Function3(par3);
//...
}
public await void Function3(string par3)
{
//...
//When it arrives here it jumps back to the first function
byte[] fileBytes = await httpClient.GetByteArrayAsync(uri);
//and after jumping back it comes back to here.
//...
}
I hope it is clear what I mean. The problem is that the jump back returns already the xml
string to the CRM application. But in that xml I need data from the async call.
How to achieve that ? Any ideas ?
Thanks a lot in advance
Michael

How can I use a C# list within signalr javascript file?

My ASP.NET project is using signalr to continuously update a web page every second with new information that enters the database. So the web page is only receiving information, not sending it. I have successfully been able to send an array from the hub towards my javascript file, and my web page correctly displayed all of the information. However, I would like to achieve the same result by using a C# list.
I have a Hub that looks like the following, and I am trying to send a C# list of items that is fetched from the GetListOfItems() method toward the javascript file:
public class DatabaseDisplayHub : Hub
{
public override async Task OnConnectedAsync()
{
await base.OnConnectedAsync();
await UpdateDatabase();
}
/// <summary>
/// Sends the updated information to the clients connected to the web interface.
/// </summary>
/// <returns></returns>
public async Task UpdateDatabase()
{
while (true)
{
await Clients.All.SendAsync("UpdateDatabase", Startup._eventsDatabase.GetListOfItems());
Thread.Sleep(1000);
}
}
}
My javascript file receives the list as a parameter to the function when a connection is established, and then tries to access several of the item's fields to display them on the HTML page.
"use strict";
// Connects to the DaabaseDisplayHub.cs to be automatically updated with the new information to display.
var connection = new signalR.HubConnectionBuilder().withUrl("/databaseDisplayHub").build();
// Called when the DatabaseDisplayHub sends the new information to display to the connected clients.
connection.on("UpdateDatabase", function (eventList) {
var eventsBody = document.getElementById("eventsBody");
while (eventsBody.firstChild) {
eventsBody.removeChild(eventsBody.firstChild);
}
eventList.foreach(eventItem => {
var eventsRow = document.createElement("tr");
eventsBody.appendChild(eventsRow);
var eventIdentifier_TD = document.createElement("td");
eventIdentifier_TD.innerHTML = eventItem.eventIdentifier;
eventsRow.appendChild(eventIdentifier_TD);
var EventActive_TD = document.createElement("td");
EventActive_TD.innerHTML = eventItem.eventActive;
eventsRow.appendChild(EventActive_TD);
var Car_TD = document.createElement("td");
Car_TD.innerHTML = eventItem.car;
eventsRow.appendChild(Car_TD);
var StartTimeStamp_TD = document.createElement("td");
StartTimeStamp_TD.innerHTML = eventItem.startTimeStamp;
eventsRow.appendChild(StartTimeStamp_TD);
var EndTimeStamp_TD = document.createElement("td");
EndTimeStamp_TD.innerHTML = eventItem.endTimeStamp;
eventsRow.appendChild(EndTimeStamp_TD);
});
});
// Called when the connection with the Hub is closed.
connection.onclose(function () {
alert("Disconnected from server.");
console.log("Database Page Disconnected.");
});
// Connects the WebSocket to the DatabaseDisplayHub.
connection.start().then(function () {
console.log("Connected to database hub");
}).catch(function (err) {
alert("Error while connecting to server.");
return console.error(err.toString());
});
I have tried debugging the javascript code through chrome developer tools, but I can't seem to understand why the list fields are not being displayed. I'm wondering whether the C# list is not compatible inside the javascript file. I have searched everywhere and can't seem to find a solution to be able to access the contents of the list.
Any help would be greatly appreciated. Thank you!
Generally, I take a C# List and serialize it like this:
Clients.Caller.ReportData(new JavaScriptSerializer().Serialize(reporthosttable));
In my client JS file receive that like this:
chat.client.reportData = function (reportData) {
var data = JSON.parse(reportData);
//do stuff with data
}
Other solutions exist I am sure... this is what works for me in a similar scenario where there are large data sets queried from a database, converted to a C# List and then send to client for display on a repeated basis.
I managed to figure this out thanks to the nudge received from one of the answers below.
I used the JsonConvert.SerializeObject() method found within the Newtonsoft.Json package to convert my C# list to a Json string.
// Within my Hub class
await Clients.All.SendAsync("UpdateDatabase", JsonConvert.SerializeObject(Startup._eventsDatabase.GetListOfItems()));
Then, within my JavaScript file, I had to parse that string using JSON.parse() for it to be in the proper form so that I can access each object's properties.
// Within my JavasScript file
connection.on("UpdateDatabase", function (eventList) {
var eventObject = JSON.parse(eventList);
var eventsBody = document.getElementById("eventsBody");
while (eventsBody.firstChild) {
eventsBody.removeChild(eventsBody.firstChild);
}
eventObject.forEach(function(eventItem) {
var eventsRow = document.createElement("tr");
eventsBody.appendChild(eventsRow);
var eventIdentifier_TD = document.createElement("td");
eventIdentifier_TD.innerHTML = eventItem.EventIdentifier;
eventsRow.appendChild(eventIdentifier_TD);
var EventActive_TD = document.createElement("td");
EventActive_TD.innerHTML = eventItem.EventActive;
eventsRow.appendChild(EventActive_TD);
var Car_TD = document.createElement("td");
Car_TD.innerHTML = eventItem.Car;
eventsRow.appendChild(Car_TD);
var StartTimeStamp_TD = document.createElement("td");
StartTimeStamp_TD.innerHTML = eventItem.StartTimeStamp;
eventsRow.appendChild(StartTimeStamp_TD);
var EndTimeStamp_TD = document.createElement("td");
EndTimeStamp_TD.innerHTML = eventItem.EndTimeStamp;
eventsRow.appendChild(EndTimeStamp_TD);
});
});

Method Prerequisites

The Situation
I'm working on a OAuth2 Api Wrapper. Some api routes are for logged people and some for anonymous and logged.
Here is an example of one method in my wrapper :
public async Task<UploadListResponse> List(bool pagination = false, int page = 1, int limit = 10)
{
var request = UploadRequests.List(pagination, page, limit);
var cancellationTokenSource = new CancellationTokenSource();
var restResponse = await Context.Client.ExecuteTaskAsync(request, cancellationTokenSource.Token);
return restResponse.Handle<UploadListResponse>();
}
I build a request with all parameter set up then execute the request and then handle the answer in case I have an api error and then output an object containing all the data that request gave me.
The problem
With OAuth2, when you log to the API you'll receive an access token and a refresh token. If your access token is expired you have to contact the api with your refresh token to get a fresh new access token.
As I said earlier some of my method needs you to be logged but if your access token is expired I want to try to refresh token before throwing an exception like with this method :
public async Task<bool> NeedRelog()
{
try
{
var validAuth = await ValidAuth();
}
catch
{
try
{
var refresh = await Refresh(Context.Client.Config.RefreshToken);
}
catch
{
return true;
}
}
return false;
}
ValidAuth check with the API if you are logged and if I have an exception then I'll try to refreshToken.
I want to tag method that need logged to call NeedRelog() and those who aren't tag to not call it.
I may just do it in every method but it wouldn't be clean.
What I've done so far
I've found a great tool : PostSharp that seems to fit my needs.
I've started to do a checkLog aspect like this :
[Serializable]
public class CheckLog : OnMethodBoundaryAspect, IOnStateMachineBoundaryAspect
{
public CheckLog()
{
ApplyToStateMachine = false;
}
public override void OnEntry(MethodExecutionArgs args)
{
var instance = (ApiService)args.Instance;
var res = instance.Parent.OAuth.NeedRelog().Result;
if (!res)
{
args.Exception = new Exception("Need to relog");
args.FlowBehavior = FlowBehavior.Return;
}
}
}
Where I'm stuck
The Main problem is with the call to my NeedRelog() Method. Due to the fact this is an async method I'm struggling to make my aspect await for it.
If my OnEntry method is async then It won't block the call if you are not logged.
If my OnEntry method is not async and I wait for needLog it freeze and nothing happen.
I really want to know to use this kind of "conditional method call" with postsharp, it looks awesome but the fact is after looking for hours in the documentation I didn't find a way to do what I want.
I'm starting to ask myself if it is even possible to achieve what I'm aiming to do.
Did you try using a way to make the call synchronous maybe with something like this stackoverflow.com/a/25097498/3131696 ? – M22an 5 hours ago
As I can't mark a comment as answering a question I quote your comment to make this question answered as it is said here : link
Thanks you for this M22an.

HttpClient ReadAsAsync does not deserialize

I am not sure if the issue I am having is related to the way I'm using Task or if I am an not using ReadAsAsync correctly. I am following the pattern I found here:
http://blogs.msdn.com/b/henrikn/archive/2012/02/11/httpclient-is-here.aspx
Background:
Object I am deserializing is a POCO. Properties have no attributes. It is just a few value type properties and a couple collection properties. REST service appears to work ok also. When I look at the JSON returned by the service it appears to be OK.
Using Web API 2.1 5.1.2
Problem:
.. is calling HttpResponseMessage.Content.ReadAsAsync(). Sometimes it works (returns an object) and sometimes it doesn't (throws "Thread was being aborted" or returns null). It appears the content property can be read once only and subsequent reads throw errors. See comments in code below.
Related questions:
HttpContent.ReadAsAsync Deserialization issue
Question appears to be similar to mine. Answer indicates a bug but this is over two years old.
Code:
[TestMethod]
public void AddSiteTest()
{
// Use POST to create a resource i.e. insert. Use PUT to update.
Site site = new Site {SiteName = "Test", Active = true, URI="www.test.com" };
Site newSite = null;
client.PostAsJsonAsync<Site>(baseURI + "/Sites/AddSite?securityKey="+ SecurityKey, site).ContinueWith(x =>
{
HttpResponseMessage response = x.Result;
response.EnsureSuccessStatusCode();
if (response.IsSuccessStatusCode)
{
try
{
string str = Task.Run(() => response.Content.ReadAsStringAsync()).Result; // yep its json and it is a proprety serialized object
// Method 1 (preferred... ):
//Site siteA = Task.Run(() => response.Content.ReadAsAsync<Site>()).Result; // usuallly throws if content has been read
// Method 2:
Site siteB = response.Content.ReadAsAsync<Site>().Result; // usully returns a valid result (when I dont put a breakpoint on it). Does not deadlock.
// Method 3:
response.Content.ReadAsAsync<Site>().ContinueWith(d =>
{
Site siteC = d.Result; // returns null
});
}
catch (Exception ex)
{
string y = ex.Message;
}
}
});
}
try to use await:
string str = await response.Content.ReadAsStringAsync();
And you have to add async before void in your method.

Pass data between methods in C#

I have the following code in my windows 8 desktop app. This gets data from a web service and populates it in a List SampleDataGroup.
protected override async void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
{
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync("http://localhost:12345/api/items");
var info = new List<SampleDataGroup>();
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
var item = JsonConvert.DeserializeObject<dynamic>(content);
foreach (var data in item)
{
var infoSect = new SampleDataGroup
(
(string)data.Id.ToString(),
(string)data.Name,
(string)"",
(string)data.PhotoUrl,
(string)data.Description
);
info.Add(infoSect);
//data=infoSect;
}
}
else
{
MessageDialog dlg = new MessageDialog("Error");
await dlg.ShowAsync();
}
}
This works fine. But I want to use the data in the above function in another function. I want to use. The properties and values I get from the web service, I would like to use in another function.
private void Item_Click(object sender, RoutedEventArgs e)
{
//Use (string)data.Name
}
In the click function I want to use the Name, as well as all other data values from the first function.
I tried setting SampleDataGroup data=null; in the code and then using data=infoSect seen commented out above and then call data.Name in the click function but it returns an null exception.
How do i pass data from one function to another?
Well currently your async method returns void, and you're not doing anything with info after populating it. It seems to me that you probably want to either return that list from the method (making it return Task<List<SampleDataGroup>> for example) or make the method set the state in an instance variable, so that it becomes part of the state of the object the method is called on.
Without more information about the class you're designing, it's hard to know whether the information is logically part of the state of the object or whether it should be returned from the method - but those are your basic choices. (There are more complicated alternatives, but those are the first two to consider.)
You should also consider what you want to happen in terms of state if there's an error - you're showing a dialog box, but what do you want to happen after that?
Simplest would be to declare a global object as below
object item;
and then change it like this
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
item = JsonConvert.DeserializeObject<dynamic>(content);
now this should be available in the click event with the latest values

Categories