Pass data between methods in C# - 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

Related

How to await multiple possibly uninitialized Tasks in C#?

I have an API POST endpoint creating a resource, that resource may have multiple relationships. To make sure the resource is created with valid relationships first I need to check if the given IDs exist. There are multiple such relations, and I don't want to await each sequentially. Here's my code:
[HttpPost]
public async Task<ActionResult<Person>> PostPerson(Person person)
{
ValueTask<Person> master, apprentice;
ValueTask<Planet> planet;
ValueTask<Models.LifeFormType> lifeFormType;
if (person.MasterId.HasValue)
{
master = _context.People.FindAsync(person.MasterId);
}
if (person.ApprenticeId.HasValue)
{
apprentice = _context.People.FindAsync(person.ApprenticeId);
}
if (person.FromPlanetId.HasValue)
{
planet = _context.Planets.FindAsync(person.FromPlanetId);
}
if (person.LFTypeId.HasValue)
{
lifeFormType = _context.LifeFormTypes.FindAsync(person.LFTypeId);
}
List<ValueTask> tasks = new List<ValueTask> {master, apprentice, planet, lifeFormType};
// if the above worked I'd process the tasks as they completed and throw errors
// if the given id was not found and such
_context.Attach(person);
// _context.People.Add(person);
await _context.SaveChangesAsync();
return CreatedAtAction("GetPerson", new { id = person.Id }, person);
}
As shown here I want to await the list of [master,apprentice,planet,lifeFormType] as they complete, but I get an error during the creation of the list that Local variable 'master' might not be initialized before accessing. So I tried in each check if the resource has that value to create an else block and somehow add a ValueTask.CompletedTask like so:
if (person.MasterId.HasValue)
{
master = _context.People.FindAsync(person.MasterId);
}
else
{
master = ValueTask.CompletedTask;
}
but then I get an error saying that Cannot convert source type 'System.Threading.Tasks.ValueTask' to target type 'System.Threading.Tasks.ValueTask<Models.Person>'.
How to do this? I guess I'll just await each and every request for now.
You can avoid this by initializing master at the declaration site.
The easiest way is using the default keyword.
ValueTask<Person> master = default;

C# BotFramework FormFlow, pass values to an external api

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

Nopcommerce Update entity issue

Using NopCommerce 3.8, Visual Studio 2015 proff.
I have created a plugin that is responsible for making restful calls to my Web API that exposes a different DB to that of Nop.
The process is run via a nop Task, it successfully pulls the data back and i can step through and manipulate as i see fit, no issues so far.
Issue comes when i try to update a record on the product table, i perform the update... but nothing happens no change, no error.
I believe this is due to the Context having no idea about my newly instantiated product object, however I'm drawing a blank on what i need to do in relation to my particular example.
Similar questions usually reference a "model" object that is part of the parameter of the method call, "model" has the method ToEntity which seems to be the answer in similar question in stack.
However my example doesn't have the ToEntity class/method possibly because my parameter is actually a list of products. To Clarify here my code.
Method in RestClient.cs
public async Task<List<T>> GetAsync()
{
try
{
var httpClient = new HttpClient();
var json = await httpClient.GetStringAsync(ApiControllerURL);
var taskModels = JsonConvert.DeserializeObject<List<T>>(json);
return taskModels;
}
catch (Exception e)
{
return null;
}
}
Method in my Service Class
public async Task<List<MWProduct>> GetProductsAsync()
{
RestClient<MWProduct> restClient = new RestClient<MWProduct>(ApiConst.Products);
var productsList = await restClient.GetAsync();
InsertSyncProd(productsList.Select(x => x).ToList());
return productsList;
}
private void InsertSyncProd(List<MWProduct> inserted)
{
var model = inserted.Select(x =>
{
switch (x.AD_Action)
{
case "I":
//_productService.InsertProduct(row);
break;
case "U":
UpdateSyncProd(inserted);
.....
Then the method to bind and update
private void UpdateSyncProd(List<MWProduct> inserted)
{
var me = inserted.Select(x =>
{
var productEnt = _productRepos.Table.FirstOrDefault(ent => ent.Sku == x.Sku.ToString());
if(productEnt != null)
{
productEnt.Sku = x.Sku.ToString();
productEnt.ShortDescription = x.ShortDescription;
productEnt.FullDescription = x.FullDescription;
productEnt.Name = x.Name;
productEnt.Height = x.Pd_height != null ? Convert.ToDecimal(x.Pd_height) : 0;
productEnt.Width = x.Pd_width != null ? Convert.ToDecimal(x.Pd_width) : 0;
productEnt.Length = x.Pd_depth != null ? Convert.ToDecimal(x.Pd_depth) : 0;
productEnt.UpdatedOnUtc = DateTime.UtcNow;
}
//TODO: set to entity so context nows and can update
_productService.UpdateProduct(productEnt);
return productEnt;
});
}
So as you can see, I get the data and pass data through to certain method based on a result. From that list in the method I iterate over, and pull the the entity from the table, then update via the product service using that manipulated entity.
So what am I missing here, I'm sure its 1 step, and i think it may be either be because 1) The context still has no idea about the entity in question, or 2) Its Incorrect calls.
Summary
Update is not updating, possibly due to context having no knowledge OR my methodology is wrong. (probably both).
UPDATE:
I added some logger.inertlog all around my service, it runs through fine, all to the point of the call of update. But again I check the product and nothing has changed in the admin section.
plugin
I have provided the full source as i think maybe this has something to do with the rest of the code setup possibly?
UPDATE:
Added the following for testin on my execute method.
var myprod = _productRepos.GetById(4852);
myprod.ShortDescription = "db test";
productRepos.Update(myprod);
This successfully updates the product description. I moved my methods from my service into the task class but still no luck. The more i look at it the more im thinking that my async is killing off the db context somehow.
Turned of async and bound the getbyid to a new product, also removed the lambda for the switch and changed it to a foreach loop. Seems to finally update the results.
Cannot confirm if async is the culprit, currently the web api seems to be returning the same result even though the data has changed (some wierd caching by deafult in .net core? ) so im creating a new question for that.
UPDATE: It appears that the issue stems from poor debugging of async. Each instance I am trying to iterate over an await call, simply put im trying to iterate over a collection that technically may or may not be completed yet. And probably due to poor debugging, I was not aware.
So answer await your collection Then iterate after.

Multiple parameters (columns rows) REST API

I'm working on a proof of concept of a RESTful API service for my employer, but have found myself slightly stuck.
I've got it working on GET-requests, both getting all the data from a view and getting some data based on parameters (id). However since the stored procedure has a TONNE of fields (140 columns) I'm a bit at a loss about how to handle any inserts / update statements that would have equally as many rows in their queries.
I've been looking around for solutions to handling multiple parameters, but I fail to see the benefit of using a "?param=args" per row. Is there a way to make the URL look slightly more formatted by passing in objects or something of the sort? If so, how would you go about (stress)testing that?
Any suggestions and advice would be greatly appreciated! :)
Example of passing data in the request body:
Creating your body content:
var example = "Hello World";
Dictionary<string, string> pairs = new Dictionary<string, string>();
pairs.Add("ExampleData", example);
var formContent = new FormUrlEncodedContent(pairs);
var response = await PostAsync(YourRouteToRestApi, formContent);
The Post method would look something like that:
public async Task<string> PostAsync(string apiCall, HttpContent data)
{
var client = new HttpClient();
try
{
var response = await client.PostAsync(apiCAll, data);
return await response.Content.ReadAsStringAsync();
}
catch (Exception ex)
{
// do something
}
}
Now you have a clean url, without hundreds of ?param=args.
If it is possible to encapsulate all your paramater into an object, or if you create a Collection which holds your parameters (like Souvik Ghosh suggested). You could pass this object or collection in your PosAsync method instead of the Dictionary data.
So the HttpClient will take care of the Json serialization.
In your WebApi you would get this data like this:
public void Post([FromBody]YourObject stuff)
{
// do something
}

How to return JavaScript results back to C# with Awesomium?

I created a new WPF project, and added a Awesomium 1.6.3 WebControl to it.
Then, I added this code to MainWindow.xaml.cs:
private void webControl1_Loaded(object sender, RoutedEventArgs e)
{
webControl1.LoadURL("https://www.google.com/");
}
private void webControl1_DomReady(object sender, EventArgs e)
{
var wc = new WebClient();
webControl1.ExecuteJavascript(jQuery);
webControl1.ExecuteJavascript(#"var __jq = jQuery.noConflict();");
webControl1.ExecuteJavascript(#"alert(__jq);");
using(var result = webControl1.ExecuteJavascriptWithResult(#"(function() { return 1; })();"))
{
MessageBox.Show(result.ToString());
}
//using (var result = webControl1.ExecuteJavascriptWithResult(#"(function() { return __jq('a'); })();"))
//{
// MessageBox.Show(result.ToString());
//}
}
And it alerts "1" and then "function (a,b){...}", which is out of order, now that I think about it, but whatever, that's another issue.
As soon as I uncomment the bottom code, it alerts "1" and then hangs. Why? How can I can some information about the links on a page? Or reliably pass some information back to C#? Or get access to the DOM with C#?
Edit: jQuery is just a string containing the jQuery 1.7 code.
Regarding why the following line hangs:
webControl1.ExecuteJavascriptWithResult(#"(function() { return __jq('a'); })();")
This is because ExecuteJavascriptWithResult can only return basic Javascript types (either a String, Number, Boolean, Array, or a user-created Object). You attempt to return a native DOM Element Object which cannot be mapped to one of these types and so the request fails.
An easy way to return complex objects would be to convert into a string using JSON.stringify(), then parse back out in your C# managed code.
For example:
JSValue rawToken = browser.ExecuteJavascriptWithResult(#"JSON.stringify(someTokenObjectHere);");
if (rawToken.IsString)
{
// For generic objects:
JObject payload = JObject.Parse(rawToken.ToString());
// For typed objects:
MyCustomTokenObject payload = JsonConvert.DeserializeObject<MyCustomTokenObject>(rawToken.ToString());
}
(It may be advantageous to include Newtonsoft.Json for the serialization stuff.)

Categories