I am using this class (representing goal) as model:
public class Goal
{
public int Id { get; set; }
public int UserId { get; set; }
public String Name { get; set; }
public List<GoalDay> Days { get; set; } = new List<GoalDay>();
public bool IsAllDayGenerated { get; set; }
public DateTime StartDateGenerated { get; set; }
public TimeSpan LengthTimeGenerated { get; set; }
public int TotalValue { get; set; }
public string Unit { get; set; }
}
Method in service sending data:
public async Task AddGoal(Goal goal)
{
var result = await _http.PostAsJsonAsync<Goal>("api/goal", goal);
if (result.StatusCode != System.Net.HttpStatusCode.OK)
{
_toastService.ShowError(await result.Content.ReadAsStringAsync());
}
else
{
_toastService.ShowSuccess(await result.Content.ReadAsStringAsync());
}
Method in controller receiving data:
[HttpPost]
public async Task<IActionResult> CreateGoal(Goal goal)
{
var user = await _utilityService.GetUser();
goal.UserId = user.Id;
_context.Goals.Add(goal);
await _context.SaveChangesAsync();
return Ok("Goal added days:" + goal.Days.Count);
}
When I send data using HttpPost to server value saved in TimeSpan (in variable LengthTimeGenerated ) is there. In the upper part of picture can be seen printscreen of network traffic.
But afterward in controller data are gone. In the lower part of the picture can be seen printscreen of model zero value of the same variable in controller.
Looks like the classic case of the camel case changes in the System.Text.Json compared to Newtownsoft. (System.Text.Json is case sensitive by default)
Try adding serialization option to use camel case.
private readonly JsonSerializerOptions _jsonSerializerOptions = new() { PropertyNameCaseInsensitive = true };
// usage
public async Task AddGoal(Goal goal)
{
var result = await _http.PostAsJsonAsync<Goal>("api/goal", goal, _jsonSerializerOptions);
//... etc
}
Other example of usage:
var myParsedObject = await JsonSerializer.DeserializeAsync<SomeResponseType>(await response.Content.ReadAsStreamAsync(), _jsonSerializerOptions);
Related
I have a small database that has been created by EF using a typical model class:
public class Metrics
{
public int Id { get; set; }
public string? MetricValue { get; set; }
public string? MetricHost { get; set; }
public string? MetricTime { get; set; }
}
The database is now populated with data and my Minimal API can return all the entries from:
app.MapGet("/metric", async (DataContext context) => await context.Metrics.ToListAsync());
And also, I can query by Id:
app.MapGet("/metric/{id}", async (DataContext context, int id) =>
await context.Metrics.FindAsync(id) is Metric metric ?
Results.Ok(metric) :
Results.NotFound("Metric not found"));
I've been searching the web for something that would show how to search by another property but have not found anything that works. e.g.,
app.MapGet("/hostnames/{MetricHost}"...
This article on CodeMaze is the closest I've found but none of the examples seem to work:
https://code-maze.com/aspnetcore-query-string-parameters-minimal-apis/
Any help is appreciated. Here's an example that did not work:
app.MapGet("/search", (SearchCriteria criteria) =>
{
return $"Host: {criteria.MetricHost}, Id: {criteria.Id}";
});
With model changes:
public class Metric
{
public int Id { get; set; }
public string? MetricValue { get; set; }
public string? MetricHost { get; set; }
public string? MetricTime { get; set; }
public static ValueTask<Metric?> BindAsync(HttpContext context, ParameterInfo parameter)
{
string hostname = context.Request.Query["MetricHost"];
int.TryParse(context.Request.Query["Id"], out var id);
var result = new Metric
{
MetricHost = hostname,
Id = id
};
return ValueTask.FromResult<Metric?>(result);
}
}
You are binding wrong type, BindAsync should be part of SearchCriteria:
app.MapGet("/search", (SearchCriteria criteria, DataContext context) =>
{
IQueryable<Metric> query = context.Metrics;
if(criteria.MetricHost is not null)
{
query = query.Where(m => m.MetricHost == criteria.MetricHost)
}
// ... rest of filters
return await query.ToListAsync();
});
public class SearchCriteria
{
public string? MetricHost { get; set; }
// ... rest of filters
public static ValueTask<SearchCriteria?> BindAsync(HttpContext context, ParameterInfo parameter)
{
string hostname = context.Request.Query["MetricHost"];
// ... rest of filters
var result = new SearchCriteria
{
MetricHost = hostname,
};
return ValueTask.FromResult<SearchCriteria?>(result);
}
}
Read more:
Filtering in EF Core
I am creating app which will comunicate with API of shop. I have written around 30 classes representing requests to API and I am wondering how to run these request parallel.
I have tried to done it with List of tasks but it does not work beacouse of imprecise returning type of function.
For example these are request classes:
public class GetOrderStatusList : IRequest<GetOrderStatusList.Response> {
public class Status {
[JsonPropertyName("id")]
public int Id { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
}
public class Response : Output {
[JsonPropertyName("statuses")]
public List<Status> Statuses { get; set; }
}
}
public class GetProductsPrices : IRequest<GetProductsPrices.Response> {
[JsonPropertyName("storage_id")]
public string StorageId { get; set; }
public class Product {
public class Variant {
[JsonPropertyName("variant_id")]
public int VariantId { get; set; }
[JsonPropertyName("price")]
public decimal Price { get; set; }
}
[JsonPropertyName("product_id")]
public int ProductId { get; set; }
[JsonPropertyName("price")]
public decimal Price { get; set; }
[JsonPropertyName("variants")]
public List<Variant> Variants { get; set; }
}
public class Response : Output {
[JsonPropertyName("storage_id")]
public string StorageId { get; set; }
[JsonPropertyName("products")]
public List<Product> Products { get; set; }
}
}
Output, IRequest and method which sends request to server:
public interface IRequest<TResponse> { }
public class Output {
[JsonPropertyName("status")]
public string Status { get; set; }
[JsonPropertyName("error_message")]
public string? ErrorMessage { get; set; }
[JsonPropertyName("error_code")]
public string? ErrorCode { get; set; }
}
public async Task<TResponse> SendRequestAsync<TResponse>(IRequest<TResponse> userRequest) where TResponse : Output {
var client = new RestClient(_url);
var method = GetRequestMethodName(userRequest);
var request = CreateRequest(method, userRequest);
var response = await ExecuteRequestAsync(client, request);
var serializedResponse = JsonSerializer.Deserialize<TResponse>(response.Content);
if( serializedResponse.Status == "ERROR") {
throw new BaselinkerException(serializedResponse.ErrorMessage, serializedResponse.ErrorCode);
}
return serializedResponse;
}
Your list type doesn't make sense; it's trying to hold items of requests for outputs, but the method just returns outputs, not requests for outputs.
Your collection type should be List<Task<Output>>, or possibly just List<Task>.
There is no nice and easy way to run multiple different tasks in a loop. Please note that this is not async/tasks specific problem: there also is no nice and easy way to run in a loop multiple non async methods that return different types.
Here's a toy example for running two tasks without waiting for one to finish before starting the next:
using System.Diagnostics;
var sw = Stopwatch.StartNew();
var t1 = F1();
var t2 = F2();
var n = await t1;
var s = await t2;
Console.WriteLine($"Elapsed {sw.ElapsedMilliseconds}");
async Task<int> F1()
{
await Task.Delay(TimeSpan.FromMilliseconds(100));
return 7;
}
async Task<string> F2()
{
await Task.Delay(TimeSpan.FromMilliseconds(200));
return "waves";
}
The elapsed time will be ~200ms (aka the time it takes for the slowest task to run), not 300ms (aka the sum of times of both tasks to run).
I'm writing a mobile app using Xamarin Forms where I am going to consume an REST API.
At the moment, I have a user model
{
public string UserId { get; set; }
public string UserDisplayName { get; set; }
public int UserRoleId { get; set; }
public string UserFirstName { get; set; }
public string UserLastName { get; set; }
public string UserEmail { get; set; }
public string UserPostcode { get; set; }
public DateTime UserCreatedAt { get; set; }
public DateTime UserModifiedAt { get; set; }
public bool UserDeletedAt { get; set; }
}
And I have defined a GetUser method on my controller
// GET: api/Users/5
[HttpGet("{id}")]
public async Task<ActionResult<User>> GetUser(string id)
{
var user = await _context.User.FindAsync(id);
if (user == null)
{
return NotFound();
}
return user;
}
If I test the API using Postman and parse the string id without quotes(edit) on the route, it works fine. E.g. https://localhost:5051/api/Users/Example. However, if I parse the id within qutoes(edit) it doesn't work: https://localhost:5051/api/Users/"Example"
My problem is, on my mobile client, when it calls the web service that calls the API, it needs to parse a string, which goes with the quotes(edit)- matching the second example.
Does any of you know a solution or a workaround for this?
Thanks in advance
EDIT:
My service method is as follows
public static async Task<IEnumerable<User>> GetUserById(string id)
{
var json = await client.GetStringAsync($"api/users/{id}");
var users = JsonConvert.DeserializeObject<IEnumerable<User>>(json);
return users;
}
And my service call is
var users = await UserService.GetUserById("Example");
EDIT2: Fixed
Service method changed to
public static async Task<User> GetUserById(string id)
{
var json = await client.GetStringAsync($"api/users/{id}");
var users = JsonConvert.DeserializeObject<User>(json);
return users;
}
It turns out the issue was caused by the IEnumerable type on the task definition, which makes sense since I was trying to retrieve a single instance.
Service method changed to
public static async Task<User> GetUserById(string id)
{
var json = await client.GetStringAsync($"api/users/{id}");
var users = JsonConvert.DeserializeObject<User>(json);
return users;
}
Currently I am doing an API call via jQuery, my question is, is there away to do this call in C# or away to convert the results of an API call to an ASP.NET List of Model Objects?
Here is my Model
public class TeamStatsClass
{
public int id { get; set; }
public string name { get; set; }
public string league { get; set; }
public string division { get; set; }
}
And here is my current ajax call
$.ajax({
url: "https://statsapi.web.nhl.com/api/v1/teams?sportId=1",
success: function (data) {
for (var team of data.teams) {
console.log(team.name);
}
}
});
UPDATE
I changed my classes to look like so:
public class StatsTeamsClass
{
public IEnumerable<Teams> teams { get; set; }
public string copyright { get; set; }
}
public class Division
{
public int id { get; set; }
public string name { get; set; }
public string link { get; set; }
}
public class Teams
{
public int id { get; set; }
public string name { get; set; }
public string link { get; set; }
public League league { get; set; }
public Division division { get; set; }
}
and created this method which indeeds puts the results in model object:
public async System.Threading.Tasks.Task<StatsTeamsClass> GetTeams()
{
HttpClient Http = new HttpClient();
var json = await Http.GetStringAsync("https://statsapi.web.nhl.com/api/v1/teams?sportId=1");
StatsTeamsClass teams = JsonConvert.DeserializeObject<StatsTeamsClass>(json);
return teams;
}
But when I try to call this method in another controller, it just hangs there, no error, no nothing, I am assuming it will just time out after a while
public class HomeController : Controller
{
APIController webService = new APIController();
public ActionResult Index()
{
var item = webService.GetTeams().Result.teams;
return View();
}
}
(GetTeams() is inside the controller APIController)
So what would be the proper way to A. get the results of an API in object model and then call those results?
The controller action needs to be made async as well to avoid mixing async-await and blocking calls like .Result or .Wait() that could potentially cause deadlocks.
Reference Async/Await - Best Practices in Asynchronous Programming
public class HomeController : Controller {
APIController webService = new APIController();
public async Task<ActionResult> Index() {
var model = await webService.GetTeams();
var teams = model.teams;
return View();
}
}
Assuming APIController is an actual ApiContoller
public class APIController : ApiController {
//Your original code
public async Task<StatsTeamsClass> GetTeams() {
HttpClient Http = new HttpClient();
var json = await Http.GetStringAsync("https://statsapi.web.nhl.com/api/v1/teams?sportId=1");
StatsTeamsClass teams = JsonConvert.DeserializeObject<StatsTeamsClass>(json);
return teams;
}
//...
}
I would suggest not calling APIController directly like that from the HomeController and instead extract the GetTeams() method out into a reusable service
public class WebService {
static Lazy<HttpClient> http = new Lazy<HttpClient>();
public async Task<T> GetAsync<T>(string url) {
var json = await http.Value.GetStringAsync(url);
return JsonConvert.DeserializeObject<T>(json);
}
public Task<StatsTeamsClass> GetTeamsAsync() {
var url = "https://statsapi.web.nhl.com/api/v1/teams?sportId=1";
return GetAsync<StatsTeamsClass>(url);
}
}
Reference You're using HttpClient wrong
that can be properly used in HomeController
public class HomeController : Controller {
public async Task<ActionResult> Index() {
// Ideally web service should be injected but that topic
// is outside of the scope of the question at the moment.
var webService = new WebService();
var model = await webService.GetTeamsAsync();
var teams = model.teams;
//...
return View(teams);
}
}
The assumption here is that the project is a mixed Asp.Net MVC and Web Api 2+
Index.cshtml
#model IEnumerable<Teams>
#{
ViewBag.Title = "Teams";
}
#if(Model != null && Model.Count() > 0) {
#foreach (var #team in Model) {
<p>#team.name</p>
}
}
Yes, the equivalent in C# would be to use HttpClient. You're best off creating a static instance of the class that you reuse for a particular kind of repeated call:
private static readonly HttpClient Http = new HttpClient();
and then used it from an async method using Newtonsoft.Json like this:
var json = await Http.GetStringAsync("https://statsapi.web.nhl.com/api/v1/teams?sportId=1");
You can then parse this string of JSON into a model class like this:
var model = JsonConvert.DeserializeObject<TeamStatsClass>(json);
As the question is answered by #Daniel above just want to add couple of more points here The json you are getting cannot be directly casted to TeamStatsClass you might have to introduce another base class as teams is the collection in the json you are getting.
Im posting it here to get a clearer view
public class ResponseBaseClass
{
public IEnumerable<TeamStatsClass> teams { get; set; }
public string copyright { get; set; }
}
public class TeamStatsClass
{
public int id { get; set; }
public string name { get; set; }
public Division division { get; set; }
}
public class Division
{
public int id { get; set; }
public string name { get; set; }
public string nameShort { get; set; }
public string link { get; set; }
}
HttpClient Http = new HttpClient();
var json = await Http.GetStringAsync("https://statsapi.web.nhl.com/api/v1/teams?sportId=1");
var model = JsonConvert.DeserializeObject<ResponseBaseClass>(json);
var yourTeamModelObj = model.teams;
I am making a simple WinForm Application in Windows and I want to get some data about foreign exchange rates. So I decided to call an API from Oanda. I tried several things around but nothing worked. It gives the response in CSV as well as JSON format. I don't know which will be easier to handle.
Also for this type of response, I am unable to create its model class.
Response:
JSON:
{
"meta": {
"effective_params": {
"data_set": "OANDA",
"base_currencies": [
"EUR"
],
"quote_currencies": [
"USD"
]
},
"endpoint": "spot",
"request_time": "2019-06-08T12:05:23+00:00",
"skipped_currency_pairs": []
},
"quotes": [
{
"base_currency": "EUR",
"quote_currency": "USD",
"bid": "1.13287",
"ask": "1.13384",
"midpoint": "1.13336"
}
]
}
CSV:
base_currency,quote_currency,bid,ask,midpoint
EUR,USD,1.13287,1.13384,1.13336
I just need those three numbers so, which method will be helpful and how.
This code I already tried:
var client = new HttpClient();
client.BaseAddress = new Uri("https://www1.oanda.com/rates/api/v2/rates/");
HttpResponseMessage response = await client.GetAsync("spot.csv?api_key=<myapikey>&base=EUR"e=USD");
string result = await response.Content.ReadAsStringAsync();
textBox1.Text = result;
Edit: I need the result of this call for my further processing so I must need this method to complete its execution before proceeding further
First creating model from Json:
use a online model generator like Json2C#, for the Json that you have posted, following is the model generated:
public class EffectiveParams
{
public string data_set { get; set; }
public List<string> base_currencies { get; set; }
public List<string> quote_currencies { get; set; }
}
public class Meta
{
public EffectiveParams effective_params { get; set; }
public string endpoint { get; set; }
public DateTime request_time { get; set; }
public List<object> skipped_currency_pairs { get; set; }
}
public class Quote
{
public string base_currency { get; set; }
public string quote_currency { get; set; }
public string bid { get; set; }
public string ask { get; set; }
public string midpoint { get; set; }
}
public class RootObject
{
public Meta meta { get; set; }
public List<Quote> quotes { get; set; }
}
Now connecting to the WebAPI using HttpClient, which has the option to return both Json and CSV, I would prefer JSON being standard, which can also be consumed easily by variety of clients, use the following simple generic methods:
Assuming it is GET only call, just supply the Host and API details to the generic Process method underneath:
public async Task<TResponse> Process<TResponse>(string host,string api)
{
// Execute Api call Async
var httpResponseMessage = await MakeApiCall(host,api);
// Process Json string result to fetch final deserialized model
return await FetchResult<TResponse>(httpResponseMessage);
}
public async Task<HttpResponseMessage> MakeApiCall(string host,string api)
{
// Create HttpClient
var client = new HttpClient(new HttpClientHandler { UseDefaultCredentials = true }) { BaseAddress = new Uri(host) };
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// Make an API call and receive HttpResponseMessage
HttpResponseMessage responseMessage = await client.GetAsync(api, HttpCompletionOption.ResponseContentRead);
return responseMessage;
}
public async Task<T> FetchResult<T>(HttpResponseMessage result)
{
if (result.IsSuccessStatusCode)
{
// Convert the HttpResponseMessage to string
var resultArray = await result.Content.ReadAsStringAsync();
// Json.Net Deserialization
var final = JsonConvert.DeserializeObject<T>(resultArray);
return final;
}
return default(T);
}
How to use:
Simply call:
var rootObj = await Process<RootObject>("https://www1.oanda.com/rates/", "api/v2/rates/");
You receive the deserialized RootObject as shown in the model above
For anything further complex processing like sending input to the call with http body, above generic code needs further modification, it is currently only specific to your requirement
Edit 1: (Making the entry call Synchronous)
To make the overall call synchronous, use the GetAwaiter().GetResult() at the topmost level, Main method will be converted to, rest all will remain same as in the sample (async methods)
void Main()
{
var rootObj = Process<RootObject>("https://www1.oanda.com/rates/", "api/v2/rates/").GetAwaiter().GetResult();
}
Edit 2: (Making complete code Synchronous)
void Main()
{
var rootObj = Process<RootObject>("https://www1.oanda.com/rates/", "api/v2/rates/");
}
public TResponse Process<TResponse>(string host, string api)
{
// Execute Api call
var httpResponseMessage = MakeApiCall(host, api);
// Process Json string result to fetch final deserialized model
return FetchResult<TResponse>(httpResponseMessage);
}
public HttpResponseMessage MakeApiCall(string host, string api)
{
// Create HttpClient
var client = new HttpClient(new HttpClientHandler { UseDefaultCredentials = true }) { BaseAddress = new Uri(host) };
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// Make an API call and receive HttpResponseMessage
HttpResponseMessage responseMessage = client.GetAsync(api, HttpCompletionOption.ResponseContentRead).GetAwaiter().GetResult();
return responseMessage;
}
public T FetchResult<T>(HttpResponseMessage result)
{
if (result.IsSuccessStatusCode)
{
// Convert the HttpResponseMessage to string
var resultArray = result.Content.ReadAsStringAsync().GetAwaiter().GetResult();
// Json.Net Deserialization
var final = JsonConvert.DeserializeObject<T>(resultArray);
return final;
}
return default(T);
}
You can use an online service such as json2csharp to get your json model and Json.Net to serialise and deserialise the json string. The following models your json.
public class EffectiveParams
{
public string data_set { get; set; }
public List<string> base_currencies { get; set; }
public List<string> quote_currencies { get; set; }
}
public class Meta
{
public EffectiveParams effective_params { get; set; }
public string endpoint { get; set; }
public DateTime request_time { get; set; }
public List<object> skipped_currency_pairs { get; set; }
}
public class Quote
{
public string base_currency { get; set; }
public string quote_currency { get; set; }
public string bid { get; set; }
public string ask { get; set; }
public string midpoint { get; set; }
}
public class RootObject
{
public Meta meta { get; set; }
public List<Quote> quotes { get; set; }
}
Note that you can change RootOject to a more descriptive name.
So, for example, to get the bid, ask and midpoint value for each quotes, you can simply do this:
RootObject rootObj=JsonConvert.DeserializeObject(jsonString);
//Get the required values.
foreach(var quote in rootObj.quotes)
{
Console.WriteLine($"Bid : {quote.bid} Ask: {quote.ask} MidPoint: {quote.midpoint}");
}