I have an Action like this:
Update([Bind(Prefix = "CurrentModel")] dynamic edited)
but when I use dynamic the ModelState.IsValid always returns true so it seems like there is no validation on the dynamic object? If not, how can I solve this?
There are two cases:
You are using view models as action arguments in which case the default model binder automatically assigns the properties and sets possible errors to the model state:
public ActionResult Update([Bind(Prefix = "CurrentModel")] EditViewModel edited)
{
if (ModelState.IsValid)
{
}
...
}
You are using some weak typing with either dynamic or FormCollection in which case the default model binder doesn't kick in and doesn't perform any validation at all as it is not capable of infering your real model type. In this case you need to manually call TryUpdateModel and indicate your model type:
public ActionResult Update(dynamic edited)
{
var model = new MyViewModel();
if (!TryUpdateModel(model, "CurrentModel"))
{
// The model was not valid
}
...
}
Conclusion: using dynamic as action argument in a controller action makes very little sense.
Related
I have a situation where i have to take input(form) from user. After continue button is pressed next view page is displayed. But after continue is pressed i don't want to store the model in the DB. I have to display some details(combining some tables) according to input given by the user earlier and again get some data from user. Only then i want to store the model in the respective tables.
How can i perform this? I tried getting Model from user and passing to the function that generates next page. Is this is way to do it? or there is other way around?
Store the model submitted by the first form in session.
[HttpPost]
public ActionResult ContinueForm1(Model1 model1)
{
if(ModelState.IsValid)
{
Session["Model1"] = model1;
return View("Form2");
}
return View();
}
[HttpPost]
public ActionResult ContinueForm2(Model2 model2)
{
if(ModelState.IsValid)
{
... model2 is already here, get the model1 from session
... and save to datatbase finally return a different view or redirect to some
... other action
}
return View();
}
You are heading down the right track.
You need to grab the model that is passed back from the first view - preferably you are using ViewModels here rather than binding directly to your db models. Have a look at http://lostechies.com/jimmybogard/2009/06/30/how-we-do-mvc-view-models/ and Why should I use view models? as to why these are good things.
The easiest way to do this is to pass the model in as an argument to your method e.g.
Assuming that your views are using the same ViewModel ( which may or may not be true) then you can send the viewmodel straight to your new view - else you can copy the elements into a new viewModel and send that.
e.g.
[HttpPost]
public ViewResult Step1(MyViewModel viewModel)
{
//Do some validation here perhaps
MySecondViewModel secondViewModel = new MySecondViewModel{
Id = viewModel.Id,
// etc. etc.
};
return View("Step2", secondViewModel);
}
Then you can carry on as you need until you have to persist the entity to the database.
NB as you do not need to do anything special in the form to make it post the model as an argument as long as the view is strongly typed to that ViewModel.
I have this post method:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Invitations(SuperInvitationsEditModel model)
{
...
var newmodel = new SuperInvitationsEditModel();
if (hasErrors)
{
SuperInvitationsErrorModel newErrorModel = new SuperInvitationsErrorModel();
newErrorModel.Errors = model.Errors;
return View(newErrorModel);
}
return View(newmodel);
}
When this code in the if(hasErrors) executes I get this error.
The model item passed into the dictionary is of type 'MyProject.Models.SuperInvitationsErrorModel', but this dictionary requires a model item of type 'MyProject.Models.SuperInvitationsEditModel'.
I thought I can do this since the return value of the method is a generic ActionResult. Can anyone tell me why is this not working?
because your current view is strongly typed. change the code as
return View("yourviewname",newErrorModel);
It has nothing to do with casting ViewResult to ActionResult. The problem is, that you have strongly typed view that expects the model of type SuperInvitationsEditModel (see #model on the top of Invitations.cshtml), but you are passing the model of type SuperInvitationsErrorModel to it.
You should merge the two view model classes (SuperInvitationsEditModel and SuperInvitationsErrorModel) into one, or create a standalone view for each of them.
I have an model being passed back successfully from my view except that I would like an ID value to be the first thing that is bound back to the Model so it can fill some information from the db. The information being passed back is as follows
SetupID:91c16e34-cf7d-e111-9b66-d067e53b2ed6
SwayBarLinkLengthLF:
SwayBarLinkLengthRF:
.....way more information....
My Action is as follows
[HttpPostAttribute]
public ActionResult SaveSetup(SetupAggregate setup)
{
setup.SaveSetup();
return null;
}
I would like the SetupID to be the first property that is set on the empty setup object but it looks like the first property alphabetically is being set first.
In my opinion you really need to be working with 2 separate "models" - your ViewModel, which is what is in your MVC project and is rendered to your view. And a 2nd EntityModel in a business logic layer. This is standard "enterprise" programming design. It gives you a lot more control of your data. The idea is this.
UI Assembly (MVC project)
ViewModel definition
public class MyModel {
public int ID { get; set; }
.... // bunch of other properties
}
Controller
public class InterestingController : Controller {
public ActionResult CreateNewWidget() {
var model = new MyModel();
return View(model);
}
[HttpPost]
public ActionResult CreateNewWidget(MyModel model) {
if(ModelState.IsValid) {
// your ctor can define the order of your properties being sent in and you can set the entity values in the ctor body however you choose to. Note never SET an ID/Primary key on a Create, let the DB handle that. If you need to return the new Key value, get it from the insert proc method in your DAL and return it up the stack
var entityModel = new EntityFromBLL(model.Name, model.OtherProperty, ... etc);
entityModel.Save(User.Identity.Name); // your save method should always capture WHO is doing the action
}
return View(model);
}
public ActionResult UpdateExistingWidget(int id) {
var entityModel = new EntityFromBLL(id); // get the existing entity from the DB
var model = new MyModel(entityModel.ID, entityModel.Name, ... etc); // populate your ViewModel with your EntityModel data in the ViewModel ctor - note remember to also create a parameterless default ctor in your ViewModel as well anytime you create a ctor in a ViewModel that accepts parameters
return View(model);
}
[HttpPost]
public ActionResult UpdateExistingWidget(MyModel model) {
if(ModelState.IsValid) {
var entityModel = new EntityFromBLL(model.ID); // always pull back your original data from the DB, in case you deal with concurrency issues
// now go thru and update the EntityModel with your new ViewModel data
entityModel.Name = model.Name;
//... etc set all the rest of the properties
// then call the save
entityModel.Save(User.Identity.Name);
}
return View(model)
}
}
Your Entity Model should be defined with private fields, public properties, a ctor that takes in all required fields for an insert (minus the primary key), a ctor that takes in the primary key and then can call an internal load method statically to return a populated object. Business rules and property validation and a single Save method. The Save method should check for the IsDirty bit after all the properties have been set and call the respective Insert or Update methods .. which in turn should call into a DAL passing DTOs
I am just new to MVC.
when we use "#Html.EditorFor" in razor view, it generates textbox.
My requirement is that I need to supply some value from viewbag or session to user's in that textbox?
Is it possible and if yes how can i do?
OR
What are the alternatives?
In your action method in the controller, pre-load a model with some data:
public ActionResult Index()
{
MyModel model = new MyModel();
model.FirstName = "Bob";
model.LastName = "Hoskins";
return View(model);
}
Then make your View strongly typed. These pre-set values should now appear on your view. You probably want to populate them from a service layer or resource file, rather than have them as hardcoded strings like my example.
ASP.NET MVC3/Razor newbie question:
I am setting up a model with custom validation. While the properties that I decorate with things like [Required] and [RegularExpression(...)] are performing as expected, I'm finding that the custom validation is not working. I made my model implement IValidatableObject, and I can hit a breakpoint inside the Validate() method and watch the method doing a yield return new ValidationResult(...); - but the form nonetheless gets posted.
Is there some secret switch that I'm missing?
If you are talking about server side validation, do you have the ModelState.Isvalid check?
http://weblogs.asp.net/scottgu/archive/2010/01/15/asp-net-mvc-2-model-validation.aspx
The form will be posted when you use IValidatableObject to validate model properties. As Joeri Jans says, you can still prevent this and return the page to the user during your action method:
public ActionResult MyAction(MyModel model)
{
if (ModelState.IsValid)
{
// code to perform action when input is valid
return [return something]
}
return View(model); // re-display form because ModelState.IsValid == false
}
If you want your custom validation to prevent the form from being posted, you need to validate on the client. The easiest way to do this is with the RemoteAttribute.
public class MyModel
{
[Remote("MyValidateAction", "MyController", HttpMethod = "POST")]
public string MyProperty { get; set; }
}
You can still keep your code in IValidatableObject, and validate it from an action method like so:
[HttpPost]
public virtual JsonResult MyValidateAction(string myProperty)
{
var model = new MyModel{ MyProperty = myProperty, };
var results = new List<ValidationResult>();
var isValid = Validator.TryValidateObject(model,
new ValidationContext(model, null, null), results, true);
return isValid
? Json(true)
: Json(results[0].ErrorMessage);
}
The above action method does virtually the same thing as the default model binder. It constructs an instance of your viewmodel, then validates it. All validation rules will be checked, including your IValidatableObject code. If you need to send more properties to the action method for the construction of your viewmodel, you can do so with the AdditionalFields property of the RemoteAttribute.
Hope this helps.