How to bind JSON response to Models in ASP MVC? - c#

So I'm new to the ASP/.NET/C# game, so would definitely appreciate any help you might be able to offer me. I've currently got a Controller called "ProjectController.cs" that has an action "Index" which is supposed to pass a list of "projects" to the view. I've gone ahead and created the controller, the action, the API call and the view. However, per the screenshots provided - I am running into an error with the API call itself - I'm attempting to return a list of models to the controller to be passed to the view. I am doing this through by loading a JSON response from the http://10000ft.com api. Any ideas why I am continually getting jammed up when trying to create/validate new project objects against the model?
ProjectController.cs:
namespace MVCProject1.Controllers
{
public class ProjectController : Controller
{
// GET: Project
public ActionResult Index()
{
List<Models.Project> projects = APICalls.GetAllProjects();
return View(projects);
}
}
}
Project Controller / Index Action View:
#{
ViewBag.Title = "Project Index";
}
#model IEnumerable<MVCProject1.Models.Project>
#foreach (var project in Model)
{
#project.name <br />
}
ProjectModel.cs:
namespace MVCProject1.Models
{
public class Project
{
public int id { get; set; }
public bool archived { get; set; }
public object archived_at { get; set; }
public string description { get; set; }
public string guid { get; set; }
public string name { get; set; }
public object parent_id { get; set; }
public object phase_name { get; set; }
public string project_code { get; set; }
public string secureurl { get; set; }
public string secureurl_expiration { get; set; }
public int settings { get; set; }
public int timeentry_lockout { get; set; }
public string ends_at { get; set; }
public string starts_at { get; set; }
public object deleted_at { get; set; }
public string created_at { get; set; }
public string updated_at { get; set; }
public bool use_parent_bill_rates { get; set; }
public string thumbnail { get; set; }
public string type { get; set; }
public bool has_pending_updates { get; set; }
public string client { get; set; }
public string project_state { get; set; }
}
}
APICalls.cs:
namespace MVCProject1
{
public class APICalls
{
private const string baseUrl = "https://api.10000ft.com/api/v1/{0}?&auth=temp12345=&{1}";
#region PROJECTS
public static List<Models.Project> GetAllProjects()
{
string requestUrl = string.Format(baseUrl, "projects", "&per_page=200");
var resultString = RestClient.makeAPICall(requestUrl);
var resultObject = JsonConvert.DeserializeObject(resultString);
List<Models.Project> projects = new List<Models.Project>();
foreach (var project in resultObject.data)
{
projects.Add(project);
}
return projects;
}
#endregion
}
}
RESTClient.cs:
namespace MVCProject1
{
public static class RestClient
{
public static string makeAPICall(string requestUrl)
{
var syncClient = new WebClient();
var content = syncClient.DownloadString(requestUrl);
Trace.WriteLine(content.ToString());
return content;
}
}
}
Image of error I'm receiving

I think that you have an issue with deserialization of product list.
Try
var projects = JsonConvert.DeserializeObject<List<Models.Project>>(resultString);
instead of
var resultObject = JsonConvert.DeserializeObject(resultString);
List<Models.Project> projects = new List<Models.Project>();
foreach (var project in resultObject.data)
{
projects.Add(project);
}
and check that projects value is not null and no errors occures while deserialization

Related

ASP.Net Core 5.0 Binding model to action with [Bind] attribute for nested child collection

I am trying to bind a model in a post action method. i.e binding with the help of [Bind] attribute.
Where I post some fields for parent while a collection of child properties at the same time.
Supose I have parent as following
class Parent
{
int field0;
string field1;
string field2;
ICollection<Child> Children;
}
class Child
{
int field3;
string field4;
string field5;
}
at the time of binding I can choose fields to bind for simple binding like [Bind("field1, field2")] and to include children as well then [Bind("field1,field2,children")]
But I need to include some fields of children like children("field4", "field5")
Is there any possibility so that I can write like following
public IActionResult UTOneFlight([Bind("field1, field2, children(field4, field5)")] Parent p)
{
}
UPDATE
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> UTOneFlight([Bind("FlightID, SrcAirportID, DestAirportID, FlightDate, Sector, RegistrationNo, FlightNo, CallSign, CrewMembers, EmbDetails, UpdateRemarks")] FlightViewModel f)
{
if (f != null && f.EmbDetails != null)
{
if (f.FlightID == 0)
{
var flight = new Flight()
{
EmbDetails = new List<EmbDetail>(),
FlightType = "emb",
AirlineOperatorID = _user.OperatorID,
SrcAirportID = f.SrcAirportID,
DestAirportID = f.DestAirportID,
FlightDate = f.FlightDate,
Sector = f.Sector.ToString().ToLower()[0],
FlightNo = f.FlightNo.Trim().ToLower(),
CallSign = f.CallSign.Trim().ToLower(),
RegistrationNo = f.RegistrationNo.Trim().ToLower(),
CrewMembers = f.CrewMembers,
UpdateRemarks = f.UpdateRemarks?? f.UpdateRemarks,
EmbDataStatus = 'u',
CreatedBy = _user.UserID
};
foreach (var e in f.EmbDetails)
{
flight.EmbDetails.Add(
new EmbDetail()
{
PaxType = e.PaxType,
PaxClass = e.PaxClass,
AdultPax = e.AdultPax,
Infants = e.Infants,
Dips = e.Dips,
FOC = e.FOC,
TransferPax = e.TransferPax,
CreatedBy = _user.UserID
}
);
}
await _db.AddAsync(flight);
return RedirectToAction("Index");
}
else
{
//var flight = await _db.SingleAsync<Flight>(x => x.FlightID == f.FlightID);
//return RedirectToAction("Index");
}
}
else
return NotFound();
}
and my models are
public class FlightViewModel
{
public long FlightID { get; set; }
public int SrcAirportID { get; set; }
public int DestAirportID { get; set; }
public string RegistrationNo { get; set; }
public string FlightNo { get; set; }
public string CallSign { get; set; }
public DateTime FlightDate { get; set; }
public int CrewMembers { get; set; }
public char Sector { get; set; }
public string UpdateRemarks { get; set; }
public ICollection<EmbDetViewModel> EmbDetails { get; set; }
}
and
public class EmbDetViewModel
{
public string PaxType { get; set; }
public char PaxClass { get; set; }
public int AdultPax { get; set; }
public int Infants { get; set; }
public int Dips { get; set; }
public int Crew { get; set; }
public int FOC { get; set; }
public int TransferPax { get; set; }
}
I need to write signature of the method like
public async Task<IActionResult> UTOneFlight([Bind("FlightID, SrcAirportID, DestAirportID, FlightDate, Sector, RegistrationNo, FlightNo, CallSign, CrewMembers, EmbDetails(PaxType, PaxClass), UpdateRemarks")] FlightViewModel f)
Please have a look at
EmbDetails(PaxType, PaxClass)
How do you send your request body? I test in my side and here's the result.
My model:
public class ParentTestModel
{
public int id { get; set; }
public ICollection<TestModel> testModels { get; set; }
}
public class TestModel
{
public string prefix { get; set; }
}
==============================Update=============================
I test in my side with [JsonIgnore] and the property which added this annotation will be ignored and this is suitable when the request body is a json object like the screenshot above. And if you are sending the request in form-data then you can use [Bind] annotation, I think you may have referred to this document.

How to generate a JSON class with dynamic name

I don't know if there is an existing name for that case, but I'm trying to retrieve data from NASA API (https://api.nasa.gov/) and I have a simple challenge to catch a list of objects near earth. Here is the JSON response I have from the GET request I do to "https://api.nasa.gov/neo/rest/v1/feed?...."
{
"links": {
"next": "http://www.neowsapp.com/rest/v1/feed?start_date=2021-07-04&end_date=2021-07-04&detailed=false&api_key=NjgpxgSbYHXyFSBI3HaOhRowtjMZgAKv2t4DMRym",
"prev": "http://www.neowsapp.com/rest/v1/feed?start_date=2021-07-02&end_date=2021-07-02&detailed=false&api_key=NjgpxgSbYHXyFSBI3HaOhRowtjMZgAKv2t4DMRym",
"self": "http://www.neowsapp.com/rest/v1/feed?start_date=2021-07-03&end_date=2021-07-03&detailed=false&api_key=NjgpxgSbYHXyFSBI3HaOhRowtjMZgAKv2t4DMRym"
},
"element_count": 6,
"near_earth_objects": {
"2021-07-03": [
{
"links": {
"self": "http://www.neowsapp.com/rest/v1/neo/3701710?api_key=NjgpxgSbYHXyFSBI3HaOhRowtjMZgAKv2t4DMRym"
},
"id": "3701710",
"neo_reference_id": "3701710",
"name": "(2014 WF497)",
"nasa_jpl_url": "http://ssd.jpl.nasa.gov/sbdb.cgi?sstr=3701710",
"absolute_magnitude_h": 20.23,
"estimated_diameter": {
"kilometers": {
}
And that's the way it is built in Visual Studio (using the Special Paste option for JSON)
public class NearEarthObject
{
public Links links { get; set; }
public int element_count { get; set; }
public Near_Earth_Objects near_earth_objects { get; set; }
}
public class Links
{
public string next { get; set; }
public string prev { get; set; }
public string self { get; set; }
}
public class Near_Earth_Objects
{
public _20210703[] _20210703 { get; set; }
}
public class _20210703
{
public Links1 links { get; set; }
public string id { get; set; }
public string neo_reference_id { get; set; }
public string name { get; set; }
public string nasa_jpl_url { get; set; }
public float absolute_magnitude_h { get; set; }
public Estimated_Diameter estimated_diameter { get; set; }
public bool is_potentially_hazardous_asteroid { get; set; }
public Close_Approach_Data[] close_approach_data { get; set; }
public bool is_sentry_object { get; set; }
}
The question is, inside of the element "near_earth_objects", there is an element called "2021-07-03" (the date of the data I requested), the problem is that I am trying to include it into a DataGridView made in .NET C# (Windows Forms, but that doesn't matters here, I think) and the user wants to get the information by date. So, "2021-07-03" is a valid member just for one day, and the user should be able to get data from multiple days.
So, is there a way in C# to get all child objects inside of near_earth_objects without knowing their names since there will be the option to search for asteroids from date X to Y in my application?
Using System.Text.Json
The API response will map to the following classes
public class Neo
{
public Links Links { get; set; }
public int ElementCount { get; set; }
public Dictionary<string, List<NearEarthObject>> NearEarthObjects { get; set; }
}
public class Links
{
public string Next { get; set; }
public string Prev { get; set; }
public string Self { get; set; }
}
public class NearEarthObject
{
public Links Links { get; set; }
public string Id { get; set; }
public string Name { get; set; }
// Other properties
}
The NearEarthObjects is simply a Dictionary, where the key is the formatted date and value is a List containing NearEarthObject
The PropertyNamingPolicy will allow us to support the API's underscore property naming convention.
public class UnderscoreNamingPolicy : JsonNamingPolicy
{
public override string ConvertName(string name)
{
return name.Underscore();
}
}
Example usage
// using using System.Text.Json;
var response = await new HttpClient().GetStringAsync(url);
var neo = JsonSerializer.Deserialize<Neo>(response, new JsonSerializerOptions
{
PropertyNamingPolicy = new UnderscoreNamingPolicy()
});
foreach(var neos in neo.NearEarthObjects)
{
Console.WriteLine(neos.Key);
}
use System.Text.Json, JsonNamingPolicy
demo code
public class DynamicNamePolicy : JsonNamingPolicy
{
public override string ConvertName(string name)
{
var today = DateTime.Today.ToString("yyyy-MM-dd");
if (name.Equals("DateData")) //model property name
return today; //convert to json string property name
return name;
}
}
//data deserialize
string data = ""; //json string
var obj = JsonSerializer.Deserialize<NearEarthObject>(data, new JsonSerializerOptions
{
PropertyNamingPolicy = new DynamicNamePolicy(),
});

Entity framework with Web Api returning object

Hi I am new to entity framework. I have following objects
public partial class lr
{
public lr()
{
this.lr_history = new HashSet<lr_history>();
this.people = new HashSet<person>();
}
public int _id { get; set; }
public string name { get; set; }
public string notes { get; set; }
public virtual ICollection<lr_history> lr_history { get; set; }
public virtual ICollection<person> people { get; set; }
In my Web Api controller I have following code
public class lrController : ApiController
{
private CTM db = new CTM();
// GET api/addL
public IEnumerable<lr> Getlr()
{
from a in DbContext.Activities
return db.lr.AsEnumerable();
}
In above Get method it returns lr but i want to return my own object like
lr.id
lr.name
lr_history.description
lrh_history.rate
Can anyone show me how to achieve this? Than
Create a new model:
class HistoryModel
{
public int Id { get; set; }
public string Name { get; set; }
public string HistoryDescription { get; set; }
public string HistoryRate { get; set; }
}
and return that:
public IEnumerable<HistoryModel> Getlr()
{
from a in DbContext.Activities
return db.lr.AsEnumerable()
.Select(x => new HistoryModel
{
Id = x.id,
Name = x.name,
HistoryDescription = x.history.description,
HistoryRate = x.history.rate
});
}
Instade of return db.lr.AsEnumerable();
try this
return db.lr.ToList();

NullReferenceException thrown by MVC View Model

I'm trying to sort out this issue but as I'm learning a lot of this stuff as I go along I'd really appreciate it if someone could explain where I'm going wrong and/or some good resources where I can read up.
So, I have a model based on my Entity Framework model of my database and a viewmodel representing properties in that model. I've built a Kendo grid to display the data (defined in a js file) and the method in the contoller returns a Json result set. Trouble is, when I try to display a value in a joined db table, if there hasn't been a key value set, I get a nullreferenceexception error. Obviously I'm missing part of the puzzle here as there must be a way of coding this to stop it happening. Any help would be gratefully received!
My model is like this:
namespace TrainingKendoUI.Models
{
using System;
using System.Collections.Generic;
public partial class TRAINING_EMPLOYEE_COURSES
{
public int EMP_COURSE_ID { get; set; }
public int EMPLOYEE_ID { get; set; }
public int COURSE_ID { get; set; }
public Nullable<System.DateTime> DATE_ATTENDED { get; set; }
public Nullable<decimal> COURSE_COST { get; set; }
public string COURSE_RESITS { get; set; }
public Nullable<int> PROVIDER_ID { get; set; }
public Nullable<int> EMP_COURSE_STATUS_ID { get; set; }
public Nullable<int> VENUE_ID { get; set; }
public virtual TRAINING_COURSES TRAINING_COURSES { get; set; }
public virtual TRAINING_EMPLOYEE_COURSE_STATUS TRAINING_EMPLOYEE_COURSE_STATUS { get; set; }
public virtual TRAINING_EMPLOYEES TRAINING_EMPLOYEES { get; set; }
public virtual TRAINING_PROVIDERS TRAINING_PROVIDERS { get; set; }
public virtual TRAINING_VENUES TRAINING_VENUES { get; set; }
}
}
My controller method looks like this:
public JsonResult EmployeeCourses_Read()
{
var model = db.TRAINING_EMPLOYEE_COURSES;
var ViewModel = new List<EmployeeCoursesIntersectionViewModel>();
foreach (var employee in model)
{
ViewModel.Add(new EmployeeCoursesIntersectionViewModel(employee));
}
return Json(ViewModel, JsonRequestBehavior.AllowGet);
}
and my view model lilke this:
namespace TrainingKendoUI.ViewModels
{
public class EmployeeCoursesIntersectionViewModel
{
#region Constructors
public EmployeeCoursesIntersectionViewModel()
{
}
public EmployeeCoursesIntersectionViewModel(TRAINING_EMPLOYEE_COURSES model)
{
this.empCourseId = model.EMP_COURSE_ID;
this.employee = model.TRAINING_EMPLOYEES.FIRST_NAME;
this.course = model.TRAINING_COURSES.COURSE_NAME;
this.dateAttended = model.DATE_ATTENDED;
this.cost = model.COURSE_COST;
this.resits = model.COURSE_RESITS;
//These lines will produce a NullReference error if not set through the front end...
this.provider = model.TRAINING_PROVIDERS.PROVIDER_NAME;
this.status = model.TRAINING_EMPLOYEE_COURSE_STATUS.EMP_COURSE_STATUS;
this.venue = model.TRAINING_VENUES.VENUE_NAME;
}
#endregion
#region Properties
public int empCourseId { get; set; }
public string employee { get; set; }
public string course { get; set; }
public Nullable<System.DateTime> dateAttended { get; set; }
public Nullable<decimal> cost { get; set; }
public string resits { get; set; }
public string provider { get; set; }
public string status { get; set; }
public string venue { get; set; }
#endregion
}
}
Do a null check on the object before setting it, i.e.
this.provider = model.TRAINING_PROVIDERS == null ? ""
: model.TRAINING_PROVIDERS.PROVIDER_NAME;
and you'll have to do similar for status and venue
this.status = model.TRAINING_EMPLOYEE_COURSE_STATUS== null ? ""
model.TRAINING_EMPLOYEE_COURSE_STATUS.EMP_COURSE_STATUS;
this.venue = model.TRAINING_VENUES== null ? ""
model.TRAINING_VENUES.VENUE_NAME;

Saving from a view model to a model in ASP.NET MVC

I have two models, a code model and a tag model which are linked by a many to many relationship. I am trying to add a code entry that includes a possible selection of many tags using a view model (using check boxes for the tags in my view). I am getting the error:
The model item passed into the dictionary is of type 'System.Collections.Generic.List'1[StoRed.Models.Code]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[StoRed.Models.CodeTagViewModel]'.
It feels like I need to somehow convert my data to the acceptable format before trying to save it into the table but I'm new to MVC and I am having trouble finding any useful information on the internet about my specific problem. Any help would be greatly appreciated.
The code model
public class Code
{
[Key]
public int CodeID { get; set; }
[Required]
[StringLength(30)]
public string Title { get; set; }
[Required]
[StringLength(150)]
public string Description { get; set; }
public DateTime DateAdded { get; set; }
public DateTime LastUpdated { get; set; }
[Required]
[StringLength(30)]
public string Project { get; set; }
[Required]
[StringLength(30)]
public string CMS { get; set; }
public int DotNetVersion { get; set; }
[Required]
[StringLength(150)]
public string Dependencies { get; set; }
[StringLength(30)]
public string Author { get; set; }
public string CodeFile { get; set; }
[Required]
[StringLength(100)]
public string TFSLocation { get; set; }
////Creates a relationship in the DB with Tag
//[ForeignKey("TagID")]
public virtual ICollection<Tag> Tags { get; set; }
////Purely for API
//[Required]
public int TagID { get; set; }
}
The Tag model
public class Tag
{
[Key]
public int TagID { get; set; }
[Required]
[StringLength(30)]
public string TagName { get; set; }
public virtual ICollection<Code> Code { get; set; }
}
The context
public class Context : DbContext
{
public DbSet<Code> Code { get; set; }
public DbSet<Tag> Tags { get; set; }
}
The view model
public class CodeTagViewModel
{
public Tag Tag { get; set; }
public Tag TagID { get; set; }
public List<Tag> Tags { get; set; }
public int CodeID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public DateTime DateAdded { get; set; }
public DateTime LastUpdated { get; set; }
public string Project { get; set; }
public string CMS { get; set; }
public int DotNetVersion { get; set; }
public string Dependencies { get; set; }
public string Author { get; set; }
public string CodeFile { get; set; }
public string TFSLocation { get; set; }
}
Relevant part of the code controller
[HttpPost]
public ActionResult Create(CodeTagViewModel codeTagViewModel)
{
if (ModelState.IsValid)
{
Code code = new Code();
Tag tag = new Tag();
var codeTag = new CodeTagViewModel();
code.Title = codeTagViewModel.Title;
code.Description = codeTagViewModel.Description;
code.DateAdded = codeTagViewModel.DateAdded;
code.LastUpdated = codeTagViewModel.LastUpdated;
code.Project = codeTagViewModel.Project;
code.CMS = codeTagViewModel.CMS;
code.DotNetVersion = codeTagViewModel.DotNetVersion;
code.Dependencies = codeTagViewModel.Dependencies;
code.Author = codeTagViewModel.Author;
code.CodeFile = codeTagViewModel.CodeFile;
code.TFSLocation = codeTagViewModel.TFSLocation;
code.Tags = codeTagViewModel.Tags;
db.Code.Add(code);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(codeTagViewModel);
}
Your best bet is to create some kind of provider/manager/service/factory/handler - choose a name that makes most sense in terms of the job it is doing within the flow of data through your system - that is responsible for taking the ViewModel and mapping the properties of the ViewModel into an instance of the domain model before persisting the domain model to the data store, either itself or by passing the hydrated domain model to a repository layer. You can either do this manually or by using something like AutoMapper. Here's a quick manual example:
Create a CommandHandlers folder in your web project with the interface and dependant handler:
public interface ICodeCommandHandler
{
int Save(CodeTagViewModel input);
}
public class CodeCommandHandler : ICodeCommandHandler
{
private IRepository<Code> repository;
public CodeCommandHandler(IRepository<Code> repository)
{
this.repository = repository;
}
public int Save(CodeTagViewModel input)
{
Code code = new Code();
Tag tag = new Tag();
code.Title = input.Title;
code.Description = input.Description;
code.DateAdded = input.DateAdded;
code.LastUpdated = input.LastUpdated;
code.Project = input.Project;
code.CMS = input.CMS;
code.DotNetVersion = input.DotNetVersion;
code.Dependencies = input.Dependencies;
code.Author = input.Author;
code.CodeFile = input.CodeFile;
code.TFSLocation = input.TFSLocation;
code.Tags.Add(tag);
return repository.Save(code);
}
}
Then in your controller, inject the ICodeCommandHandler in via constructor injection, the same as you do with the repository in the CodeCommandHandler:
private readonly ICodeCommandHandler commandHandler;
public CodeController(ICodeCommandHandler commandHandler)
{
this.commandHandler = commandHandler;
}
[HttpPost]
public ActionResult Create(CodeTagViewModel codeTagViewModel)
{
if (!ModelState.IsValid)
{
return View(codeTagViewModel);
}
var id = codeCommandHandler.Save(codeTagViewModel);
// maybe do something useful with the document id after save
return RedirectToAction("Index");
}
To keep the Repository nice and simple, here's how that could look:
public interface IRepository<T>
{
int Save(T entity);
}
public class CodeRepository : IRepository<Code>
{
public int Save(Code entity)
{
using (var context = new Context())
{
context.Code.Add(entity);
context.SaveChanges();
}
}
}
I've not gone into detail about the dependency injection side of things as that wasn't part of the question but this should give you an idea of where to start

Categories

Resources