I have a controller action method
[HttpPost]
public ActionResult Create(MyItem item){
...
}
My type definitions look like this
public class MyItem{
...
public List<MySubItem> MySubItems{ get; set; }
}
public class MySubItem{
...
}
I have created a custom model binder for List<MySubItem> and registered it
ModelBinders.Binders.Add(typeof(List<MySubItem>), new MySubItemsModelBinder());
I was expecting that when the automatic model binder was binding MyItem it would use MySubItemsModelBinder when it was binding the List<MySubItem> MySubItems property of MyItem but it does not.
What Im wondering is if my expectation is incorrect or if I have misconfigured somewhere?
Related
I'm new to asp.net mvc. Basically i'm from php programmer. In my php file i can display what are all the values coming from html page or form using echo $_POST; or print_r($_POST); or var_dump($_POST). But in asp.net how can i achieve this to check what are all the values are coming from UI Page to controller.
You may take a look at the Request.Form property:
public ActionResult SomeAction()
{
var values = Request.Form;
...
}
You could put a breakpoint and analyze the values. Or simply use a javascript development toolbar in your browser (such as FireBug or Chrome Developer Toolbar) to see exactly what gets sent to the server.
But normally you are not supposed to directly access the raw values. In ASP.NET MVC there's a model binder which could instantiate some model based on the values sent to the server.
For example you could have the following model:
public class MyViewModel
{
public int Age { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
and then have your controller action take this model as parameter:
public ActionResult SomeAction(MyViewModel model)
{
... you could use the model properties here
}
and now you could invoke this controller action either wityh a GET request passing the parameters in the query string (/someaction?age=10&firstname=foo&lastname=bar) or using a POST and sending them in the body.
You can check the raw data via Request.Form.
But this is not he spirit of the ASP.NET MVC. It is preferd that you expect a model into your controller. You have all type safety mapping already done by special module called model binder.
So unless you work on some special case, you just add a model to the controller action:
public ActionResult SomeAction(SomeModel model)
{
//Handle SomeModel data further ...
}
You can create an action which will accept the parameters from the UI page like the following:
[HttpPost]
public ActionResult SomeAction(string param1, int param2)
{
//Now you can access the values here
}
or make an action which will accept the model
public ActionResult SomeAction(SomeModel model)
{
//Access the model here
}
I am trying to use fluent validation with ASP.NET MVC project. I am trying to validate my view model.
This is my viewmodel,
[Validator(typeof(ProductCreateValidator))]
public class ProductCreate
{
public string ProductCategory { get; set; }
public string ProductName { get; set; }
....
}
This is my validator class,
public class ProductCreateValidator : AbstractValidator<ProductCreate>
{
public ProductCreateValidator()
{
RuleFor(product => product.ProductCategory).NotNull();
RuleFor(product => product.ProductName).NotNull();
}
}
And in my controller, I am checking whether my ModelState is valid or not,
[HttpPost]
public ActionResult Create(ProductCreate model)
{
/* This is a method in viewmodel that fills dropdownlists from db */
model.FillDropDownLists();
/* Here this is always valid */
if (ModelState.IsValid)
{
SaveProduct(model);
return RedirectToAction("Index");
}
// If we got this far, something failed, redisplay form
return View(model);
}
This is what I have. My problem is ModelState.IsValid returns true when my viewmodel is completely empty. Do i need to manually configure Fluent validation so that model errors can be added to ModalState ?
As the documentation explains, make sure you have added the following line in your Application_Start in order to swap the data annotations model metadata provider and use fluent validation instead:
FluentValidationModelValidatorProvider.Configure();
Also the following comment in your action scares me:
/* This is a method in viewmodel that fills dropdownlists from db */
model.FillDropDownLists();
A View model shouldn't know what a database means. So having such methods in your view model is a very wrong approach.
I have a view model
public class ViewModel
{
public string Text { get; set; }
public string Name { get; set; }
}
The submited form provides only the Text value. I'd like to set the the Name property in my custom model binder.
So I derived my custom model binder from the DefaultModelBinder class and overrided the BindModel method.
The problem is that the BindModel method is called only for the incomning properties.
My question is how can I set the Name value in my cystom model binder ?
If you do not have an incoming value for Name, then you are not doing (custom) model binding. Instead, you want to supply some data in your model object before the action executes, right ? If so, use ActionFilter for it, override OnActionExecuting() and supply the data you need into action parameters.
public class SupplyNameAttribute : FilterAttribute, IActionFilter
{
public void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.ActionParameters != null)
{
foreach (KeyValuePair<string, object> parameter in filterContext.ActionParameters)
{
if (parameter.Key == "Name") parameter.Value == "Hey";
}
}
}
}
EDIT :
You can also use custom ValueProvider for default model binding, see
http://mgolchin.net/posts/19/dive-deep-into-mvc-ivalueprovider
I'm trying to create a wizard-like workflow on a site, and I have a model for each one of the steps.
I have the following action methods:
public ActionResult Create();
public ActionResult Create01(Model01 m);
public ActionResult Create02(Model02 m);
public ActionResult Create03(Model03 m);
And I want the user to see the address as
/Element/Create
/Element/Create?Step=1
/Element/Create?Step=2
/Element/Create?Step=3
All the model classes inherit from a BaseModel that has a Step property.
The action methods that have the parameters have the correct AcceptVerbs constraint.
I tried naming all the methods Create, but that resulted in a AmbiguousMatchException.
What I want to do now is to create a custom route for each one of the actions, but I can't figure out how to do it.
This is what I tried:
routes.MapRoute(
"ElementsCreation",
"Element/Create",
new{controller="Element", action="Create01"},
new{Step="1"}
);
But this doesn't work.
Any help (on the correct MapRoute call or maybe a different approach) would be greatly appreciated.
Thanks
I actually found a different approach.
Instead of adding a new Route Map, I created a new Action Method attribute to verify if the passed request is valid for each of the action methods.
This is the attribute class:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class ParameterValueMatchAttribute : ActionMethodSelectorAttribute
{
public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
{
var value = controllerContext.RequestContext.HttpContext.Request[Name];
return (value == Value);
}
public string Value { get; set; }
public string Name { get; set; }
}
And I have each one of the action methods with the same name and decorated like this:
[AcceptVerbs(HttpVerbs.Post)]
[ParameterValueMatch(Name="Step", Value="1")]
public ActionResult Create(Model01 model)
I like this approach a LOT more than creating one route for each method.
I have implemented a custom membership provider and have the following class;
public class ProfileCommon : ProfileBase
{
#region Members
[Required(ErrorMessage="Required")]
public virtual string Title
{
get { return ((string)(this.GetPropertyValue("Title"))); }
set { this.SetPropertyValue("Title", value); }
}
I then, in my controller want to do the following;
[HttpPost]
[Authorize]
public ActionResult EditInvestorRegistration(FormCollection collection)
{
ProfileCommon profileCommon= new ProfileCommon();
TryUpdateModel(profileCommon);
This kinda fails when title is not included with the error;
Property accessor 'Title' on object 'Models.ProfileCommon' threw the following exception:'The settings property 'Title' was not found.'
If I get rid of the attribute [Required... it works fine but now I no longer have automatic validation on my object.
Now, I know I could check each property at a time and get around the issue but I'd dearly like to use DataAnnotations to do the work for me.
Any ideas?
It seems strange that you are using a custom profile class as action input instead of a view model:
public class ProfileViewModel
{
[Required]
public string Title { get; set; }
}
and then in your controller you could use AutoMapper to convert between the view model and the model class which will update the profile:
[HttpPost]
[Authorize]
public ActionResult EditInvestorRegistration(ProfileViewModel profileViewModel)
{
ProfileCommon profileCommon = AutoMapper.Map<ProfileViewModel, ProfileCommon>(profileViewModel);
...
}