Passing complex object with RouteValueDictionary using RedirectToAction - c#

I'm trying to pass a Complex Object(Object within an object) to my action method using RedirectToAction method but it is returned null. Is there a way to do this using RouteValueDictionary?
My Model:
public class ModelUser
{
public UserLine User { get; set; }
}
public class UserLine
{
public int? UserID { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
}
My Action method:
[HttpPost]
public async Task<ActionResult> Create(UserCreate model)
{
var valDTORegister = new RegisterLine() { Email = model.Email, Password = model.Password, OwnerType = model.OwnerType };
var result = await PostRequest<RegisterLine, UserLine>("http://localhost:10853/", "api/user/Create", valDTORegister);
var usermodel = new ModelUser();
usermodel.User = result;
return RedirectToAction("ProfileUser", new RouteValueDictionary(usermodel));
}
public ActionResult ProfileUser(ModelUser usermodel) //User object is null
{
return View();
}
I tried passing only UserLine object using RouteValueDictionary and the values has been properly passed to my ProfileUser method. This is good but I want to pass the whole ModelUser object because I may need to add more object within it.

you can connect the ProfileUser page to model ModelUser and than call this view like this:
return View("ProfileUser", usermodel);
the action shold be without parameters
public ActionResult ProfileUser()
{
return View();
}
And all data is in the model ("ModelUser") of ProfileUser

Related

Get json data from POST method in asp.net mvc

I got an error while getting json data from POST method, am I doing something wrong
C# Code:
public IActionResult signupapi(UserSignUp user)
{
var model = new Models.SignUpModelAPI(HttpContext);
if (user == null)
{
return Content(model.ResponseJsonText(false, string.Format(model.Language("empty"),
HttpContext.Request.Method, HttpContext.Request.Path.Value), Class.CodeResponse.ERROR), new Microsoft.Net.Http.Headers.MediaTypeHeaderValue("application/json"));
}
if (!model.isAllowMethod("POST"))
{
return Content(model.ResponseJsonText(false,string.Format(model.Language("notallowmethod"),
HttpContext.Request.Method,HttpContext.Request.Path.Value),Class.CodeResponse.ERROR),new Microsoft.Net.Http.Headers.MediaTypeHeaderValue("application/json"));
}
return Content(JsonConvert.SerializeObject(user));
}
public class UserSignUp
{
public string fullname { get; set; }
public string username { get; set; }
public string email { get; set; }
public string password { get; set; }
}
And this is the result when i try on reqbin every value i get is null
You need to add FromBody attribute to get your data for the POST operation:
public IActionResult signupapi([FromBody]UserSignUp user)
You can read more on parameter binding on MSDN docs.

ASP.NET - API Url to List of Model Objects

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;

Remove functionality from ASP.NET MVC controller

I have a controller which is used to save data in database. The controller looks like below:
[Authorize]
[HttpPost]
public ActionResult Create(EmployeeFormViewModel viewModel)
{
var _employee = new Employee
{
Employee = User.Identity.GetUserId(),
DateTime = DateTime.Parse(string.Format("{0} {1}", viewModel.Date, viewModel.Time))
};
_context.Employees.Add(_employee);
_context.SaveChanges();
return RedirectToAction("Index", "Home");
}
I want to remove this line of code
DateTime.Parse(string.Format("{0} {1}", viewModel.Date, viewModel.Time))
and make this calculations somewhere else in order to keep the controller clean.
Which is the best way to archive this?
From the data given I see that you have used a ViewModel called EmployeeFormViewModel to saperate the logic from the model. I would guess that your ViewModel looks something like below:
public class EmployeeFormViewModel
{
public string Venue { get; set; }
public string Date { get; set; }
public string Time { get; set; }
}
Now, in order to make the changes in controller, i would suggest you make it look like below:
[Authorize]
[HttpPost]
public ActionResult Create(EmployeeFormViewModel viewModel)
{
var _employee = new Employee
{
Employee = User.Identity.GetUserId(),
DateTime = viewModel.DateTime
};
_context.Employees.Add(_employee);
_context.SaveChanges();
return RedirectToAction("Index", "Home");
}
and after that go to your ViewModel and add the new property that you added in the Controller (DateTime). Now your ViewModel should look something like below:
public class EmployeeormViewModel
{
public string Venue { get; set; }
public string Date { get; set; }
public string Time { get; set; }
public DateTime DateTime
{
get
{
return DateTime.Parse(string.Format("{0} {1}", Date, Time));
}
}
}
I hope this solves your problem.
To offer a different perspective, I'd suggest you could put it in an extension method. The concept of combining date and time strings doesn't really feel like it should belong to your domain model, it feels like a generic thing that you might want to use across your application (or even in other applications). I would do this...
public static class DateTimeExtensions
{
public static DateTime ParseToDateTime(this string date, string time = null)
{
return string.IsNullOrEmpty(withTime) ? DateTime.Parse(date) : DateTime.Parse($"{date} {time}");
}
}
And in the controller...
[Authorize]
[HttpPost]
public ActionResult Create(EmployeeFormViewModel viewModel)
{
var _employee = new Employee
{
Employee = User.Identity.GetUserId(),
DateTime = viewModel.Date.ParseToDateTime(viewModel.Time)
};
EDIT: Additionally...to incorporate etr's answer, which is also a good approach, you could combine the two...
public class EmployeeormViewModel
{
public string Venue { get; set; }
public string Date { get; set; }
public string Time { get; set; }
public DateTime DateTime
{
get
{
return Date.ParseToDateTime(Time);
}
}
}
Rich domain is the way.
public class Employee
{
public Employee(int id, object date, object time)
{
Id = id;
DateTime = DateTime.Parse(string.Format("{0} {1}", date, time))
}
public int Id { get; protected set; }
public DateTime DateTime { get; protected set; }
}
And them:
[Authorize]
[HttpPost]
public ActionResult Create(EmployeeFormViewModel viewModel)
{
_context.Employees.Add(new Employee(User.Identity.GetUserId(), viewModel.Date, viewModel.Time));
_context.SaveChanges();
return RedirectToAction("Index", "Home");
}
I like strong type binding and a Post method as follows:
public ActionResult Create(EmployeeFormViewModel viewModel)
{
viewModel.Post(User.Identity.GetUserId());
_context.Employees.Add(_employee);
_context.SaveChanges();
return RedirectToAction("Index", "Home");
}
The view model looking like this:
public class EmployeeFormViewModel
{
Employee Employee { get; set; }
DateTime Date { get; set; }
DateTime Time { get; set; }
public void Post(int empid)
{
Employee= new Employee
{
EmployeeID = empid,
DateTime = DateTime.Parse(string.Format("{0} {1}", Date, Time))
};
return;
}
}
This is all possible because of the nice MVC Binding engine which generates the EmployeeFormViewModel based on query strings, prior to calling the action method.
I put a "Post" method in all of my ViewModels and let MVC do the work.

TempData not passing object

I have this method:
[HttpPost]
public async Task<ActionResult> AddBookingCreditCard(TripViewModel model)
{
if (ModelState.IsValid)
{
// do nothing right now
}
TempData["ccInfo"] = model.CcInfo;
return RedirectToAction("Book", new{ id = model.Trip.TripId, travellers = model.Travellers});
}
In this case, model.CcInfo DOES exist and contains an object of type CcInfo. After the RedirectToAction, this method is called:
[HttpGet]
public async Task<ActionResult> Book(int id, int travellers)
{
var vm = new TripViewModel();
vm.Travellers = travellers;
if (TempData["ccInfo"] != null)
vm.CcInfo = TempData["ccInfo"] as CcInfo;
// lot of code
return View("Index", vm);
}
The if-statement always fails: TempDate["ccInfo"] is null, while it should contain my object.
What am I doing wrong?
EDIT:
The CcInfo class:
public class CcInfo
{
[Display(Name = "Name on Card"), Required]
public string CcName { get; set; }
[Display(Name = "Card Number"), DataType(DataType.CreditCard), Required]
public int CcNumber { get; set; }
[Display(Name = "Card CVV"), Required, Range(100, 999)]
public int CcCheck { get; set; }
}
I've tried to replicate your issue, and I can spot the problem.
This code works fine for me:
public class FooController : Controller
{
public async Task<ActionResult> AddBookingCreditCard()
{
TempData["ccInfo"] = "Hell world";
return RedirectToAction("Book", new { id = 1, travellers = 2 });
}
[HttpGet]
public async Task<ActionResult> Book(int id, int travellers)
{
var vm = new object();
if (TempData["ccInfo"] != null)
{
vm = new object();
}
// lot of code
return new EmptyResult();
}
}
However, if I change AddBookingCreditCard to recieve POST requests only:
[HttpPost]
public async Task<ActionResult> AddBookingCreditCard()
Then the TempData dictionary is empty.
To cut to the chase, you need to use session state for storing items between requests in your case.
I don't recall the exact details, but I think TempData does work well for situations where you need to redirect to another controller action, but only if it's within a GET request. Coming from a POST and then redirecting to a GET seems to wipe TempData to a clean slate, which makes sense, as TempData is for that scenario only I believe (please feel free to correct me on that).

How can I call my model from controller?

I am attempting to call my model from my controller, but get errors. The error is on
new UserModels(id, searchcriteria);
and states that
UserModels does not contain a constructor that takes 2 arguments.
Any ideas?
Controller/Action:
public ActionResult ID(string id)
{
ViewBag.Message = "Customer Information";
string searchcriteria = "userid";
UserModels model = new UserModels(id, searchcriteria);
return View();
}
Model:
public class UserModels
{
public UserData user { get; set; }
public string firstname { get; set; }
public string lastname { get; set; }
public string searchvalue {get; set; }
public string searchcriteria { get; set; }
public List<UserData> UserModel(string id, string searchcriteria)
{
SSO_Methods sso = new SSO_Methods();
List<UserData> userObject = sso.GetUserObject(id, searchcriteria);
return userObject;
}
}
Constructors in c# cannot return anything.
Your code would need to be
public UserModels(string id, string searchcriteria)
{
// your code here
}
Then if you are wanting to return a list, add in
public List<UserData> GetUserModels(string id, string searchcriteria)
{
SSO_Methods sso = new SSO_Methods();
List<UserData> userObject = sso.GetUserObject(id, searchcriteria);
return userObject;
}
You have to create a constructor which has 2 parameters. To create a constructor you have to write something like this: [public, private, protected, internal] [classname]([parameters]).
So just change this: public List<UserData> UserModel(string id, string searchcriteria)
To public UserModel(string id, string searchcriteria). Notice that a constructor must not return anything. Use properties instead of your return type.

Categories

Resources