mvc: Model.IsValid not working if Iam using Request.Form - c#

Iam a beginner in MVC.
If iam using below code then Model.IsValid is not validating the object which in this case is Customer.
public ActionResult Submit()
{
Customer custObj = new Customer();
custObj.CustomerCode = Request.Form["CustomerCode"];
custObj.CustomerName = Request.Form["CustomerName"];
if (ModelState.IsValid)
return View("Load", obj);
else
return View("EnterCustomer");
}
While if Iam passing the Customer object in parameter then Model.IsValid is working perfectly.
public ActionResult Submit(Customer obj)
{
//Customer custObj = new Customer();
//custObj.CustomerCode = Request.Form["CustomerCode"];
//custObj.CustomerName = Request.Form["CustomerName"];
if (ModelState.IsValid)
return View("Load", obj);
else
return View("EnterCustomer");
}
Can any1 help me in getting to know the reason.

It doesn't work beause MVC never bound to the model itself. You manually overrode it so MVC has no clue whether the model is valid or not. It doesn't event know that custObj is the model.
ModelState.IsValid is set before your action method is called, so in your second example, when you allow MVC to bind to the model itself, it works. In the first, it doesn't work because you create the model and do manual binding to it.
Update
You can, however, also manually run the model validation by calling ValidateModel or TryValidateModel on the controller.
Documentation:
ValidateModel: https://msdn.microsoft.com/en-us/library/system.web.mvc.controller.validatemodel(v=vs.100).aspx
TryValidateModel: https://msdn.microsoft.com/en-us/library/system.web.mvc.controller.tryvalidatemodel(v=vs.100).aspx

As mentioned in other answers, your model is already validated before the action 'Submit' is called. So, when you are changing the model from inside your action, you will have to manually validated the model. You may use below code for it.
var context = new ValidationContext(custObj, serviceProvider: null, items: null);
var validationResults = new List<ValidationResult>();
bool isValid = Validator.TryValidateObject(custObj, context, validationResults, true);
if (isValid)
return View("Load", obj);
else
return View("EnterCustomer");
use below url for further details.
http://odetocode.com/blogs/scott/archive/2011/06/29/manual-validation-with-data-annotations.aspx

Related

How to pass parameter on RedirectUrl to new ActionResult

I have this code snippet in my Controller class:
var userId = _userService.GetUserByActivationCode(model.ActivationCode);
if (userId != null)
{
var verifiedUser = VerifyUserActivationCode(model.ActivationCode, model.DateOfBirth);
resultModel.RedirectUrl = Url.Action(AccountInformationForUserJourneyAction, new { user = verifiedUser});
}
verifiedUser is populated correctly, but when passed to this action, user is null.
[AllowAnonymous, HttpGet]
public ActionResult AccountInformationForUserJourney(User user) { // code here }
Why is the verifiedUser not passed to the new ActionResult?
Change
resultModel.RedirectUrl = Url.Action(AccountInformationForUserJourneyAction, new { user = verifiedUser});
To
resultModel.RedirectUrl = Url.Action(AccountInformationForUserJourneyAction, verifiedUser);
You are not binding a model to a view, you are creating a URL that will, in this case, need a querystring to pass any data to the new action. If you want to POST a model, you would need a form with a Javascript auto-post function. Anything you pass in the querystring needs to be URL safe and will then bind to the new action using name=value.
This doesn't seem like what you want to do. Either just pass a user id that you can reload in the new action or return the view that you want the user to see (AccountInformation) directly from the action you are already in, then you can bind your model directly to the view.

How to get the PK of a new item created in the database to use right away and update it's settings

I am using a web API with ASP.NET Core MVC, entityframework and angular as the front-end.
In my angular application I have Stepper component https://material.angular.io/components/stepper
In the first step I want to fill out my form and as soon as I click next I want to create the task and on the second form I want to update the settings for that newly created task. However, I need to get the PK of the newly created task to update the settings on my second form.
There is an api call to create a new task in the tTask table in sqlserver.
This is my API Post Method.
[ResponseType(typeof(CreatingTaskModel))]
public IHttpActionResult PosttTask(CreatingTaskModel CreatingTaskModel)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var newtTask= new tTask
{
TaskName = CreatingTaskModel.TaskName,
TaskDescription = CreatingTaskModel.TaskDescription,
EmailSubject = CreatingTaskModel.EmailSubject,
EmailBody = CreatingTaskModel.EmailBody,
FK_TaskTeam = CreatingTaskModel.tTaskTeam.pk_TaskTeam,
Enabled = CreatingTaskModel.Enabled ? 1:0,
};
db.tTasks.Add(newtTask);
db.SaveChanges();
var pk = new {id = CreatingTaskModel.PK_Task};
return Ok("Success");
}
This is the submit code in angular :
onSubmit()
{
if(this.taskForm.valid)
{
this.service.createTask(this.taskForm.value).subscribe((data) => {
debugger;
console.log(data)
})
}
}
I saw that return Ok() in my webapi returns a message and I was thinking of sorting the tTask table by decending order of after the db.SaveChanges();
and returning the last item that it find and sending it back in the Ok(pk) and then casting that into an integer on my client-side and using that to get the data to update it.
What is the correct way to do this? Should it be done in sql or on the webapi?
This is what I ended up doing:
var newObject = new
{
id = newtTask.PK_Task,
message = "Success"
};
return Ok(newObject);
and on angular I have this:
onSubmit(){
if(this.taskForm.valid) {
this.service.createTask(this.taskForm.value).subscribe((data) => {
if(data.message){
if(data.message == "Success"){
this.getRecipentData(data.id);
this.AdditionalRecipientForm.controls['FK_Task'].setValue(data.id);
this.pk_Task = data.id;
}
}
debugger;
console.log(data)
})
}
It just doesn't seem practical to do this, but it does the job. What do you guys think? Should I maybe instead of going to the serve twice maybe after it goes to the done filling out both forms submit them both? Like call create method in my API and then call my second API to update the data the was submitted in the second form. I am just looking for ideas or most common practice for these types of situations.
After you've added it to the database and called db.SaveChanges, the key will be assigned to the object. So, after db.SaveChanges, you should just be able to simply reference newtTask.Id.
So I assume you manually assign the id of the new task via PK_Task = CreatingTaskModel.PK_Task, and that it's indeed this id that gets saved to the Db. Therefore the id column should not be autoincrementing.
If that's true you can, as Louis said before, simply return your object or only the id if you're concerned about bandwidth.
You can do return new OkObjectResult(pk); or just use the shorthand: return Ok(pk);, which will also return an OkObjectResult with the pk object as payload.

Directly call ASP.NET controller action from a different controller

I have a situation where I need to redirect to an ASP.NET MVC action in a different controller. I can't use RedirectToAction because I must POST the action's parameters to keep them out of the URL.
I attempted to instantiate and call the other controller's action directly like this:
OtherController myOtherController = new OtherController();
myOtherController.ControllerContext = new ControllerContext(this.ControllerContext.RequestContext, myOtherController);
return await myOtherController.Edit(myGuid);
When I do this, the other controller's code executes, but I end up with this error:
The model item passed into the dictionary is of type
'System.Data.Entity.DynamicProxies.OtherModel_BBCEF7C9378F4C4F097CC08FA2E508B8BD8D865E33093E31959919087A31348E',
but this dictionary requires a model item of type 'ThisModel'.
Does anyone know if I can get this working using the current approach? Assuming I must use an HTTP POST action and cannot have parameters in the URL, is there a different approach that you would recommend to achieve this result (other than combining the controllers)?
Edit:
Note that I don't think I can post directly from the client because I would need to nest Html.BeginForm.
You should really just be using RedirectToAction to push the browser to the action you want instead of trying to do it like this. As you can see from #Andrei Olariu code a lot of things happen under the hood during the construction of your controller (DI then context and parameter mappings) that really shouldn't be done manually and can easily be screwed up leading to hours wasted wondering why certain things are not behaving as expected.
I hope I understood what you're trying to do. This is what I'm successfully using to execute an action on a different controller.
private void ExecuteErrorAction(string action, HttpContextWrapper httpContext, Exception exception)
{
var routeData = new RouteData();
routeData.Values["controller"] = "Error";
routeData.Values["action"] = action;
routeData.Values["exception"] = exception;
IController errorController = DependencyResolver.Current.GetService<ErrorController>();
var context = new RequestContext(httpContext, routeData);
errorController.Execute(context);
}
I think that in your case this could look like:
private void ExecuteOtherAction(string myGuid, HttpContextWrapper httpContext)
{
var routeData = new RouteData();
routeData.Values["controller"] = "OtherController";
routeData.Values["action"] = "Edit";
routeData.Values["myGuid"] = myGuid;
IController otherCntroller = DependencyResolver.Current.GetService<OtherController>();
var context = new RequestContext(httpContext, routeData);
otherCntroller.Execute(context);
}
This is assuming that your Edit action on the OtherController takes a string called myGuid as a parameter.
Let me know if this helps.

should this mvc action be trimmed down

I'm just getting into mvc 4 (and mvc in general) and am just wondering is this action code ok or should it be stripped down again?
[HttpPost]
public ActionResult Index(DashboardViewModel dbModel)
{
//retrieve latest resident order
var residentOrder = db.ResidentOrders.GetById(dbModel.ResidentOrderID);
if (residentOrder == null)
{
var order = db.Orders.GetById(dbModel.OrderID);
var user = db.Users.GetUserByUsername(User.Identity.Name);
residentOrder = new ResidentOrder()
{
CreatedDate=DateTime.Now,
LastUpdateDate = DateTime.Now,
Litres=0,
Customer = user
};
order.ResidentOrders.Add(residentOrder);
db.Commit();
}
//check to see if value has changed
if (!dbModel.ResidentLitresOrdered.Equals(residentOrder.Litres))
{
//get new ordered value
residentOrder.Litres = dbModel.ResidentLitresOrdered;
db.Commit();
//send an email just to notify in writing of the change.
SendOwnOrderQtyUpdateNotification();
}
return View(dbModel);
}
Basically if a resident order doesnt exist then we create one, this is the only place in the system where this would need to happen.
Should I still be stripping that code out into my repositories?
db is my IUnitOfWork btw
I would recommend that you create a "repository" to hide the details from the Controller action.
An "Upsert" method would allow this to be clearly and elegantly implemented hiding the details from the controller.

Asp.Net Mvc - Don't clear error input in form

When I validate a form with Asp.Net Mvc and there's an error on the form, Asp.Net return me the form with the correct error message but clear the content of the input. I would like the content of the input to stay even if this content is wrong. How can I do that ?
UPDATE
Maybe this is because I don't use the default validation. I use the Fluent Validation library.
Here what I do :
var validator = new UserValidator();
var results = validator.Validate(user);
results.AddToModelState(ModelState, "");
if (!ModelState.IsValid)
{
return View("SignUp", user);
}
The problem might be in how you "bind" the model you are passing in the view. If you use a strongly typed view and create the input fields with for example
<%=Html.TexboxFor(m=>m.UserName)%>
or
<%=Html.TextBox("UserName", Model.UserName)%>
then you should see the values after posting.
Regards
In addition to what #Germán Ubillos posted, you can also store the post values in TempData and send them back through.
<%=Html.TextBox("UserName", TempData["UserName"])%>
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult SomeAction(string UserName)
{
TempData["UserName"] = UserName;
// Do your validation
var validator = new UserValidator();
var results = validator.Validate(user);
results.AddToModelState(ModelState, "");
if (!ModelState.IsValid)
{
return View("SignUp", user);
}
//return some view
}

Categories

Resources