ActionResult parameters with no default constructor - asp.net

Obviously there are a number of ways to do this, but I thought I'd ask for a little feedback on benefits and drawbacks of the approaches.
First of all, the NerdDinner tutorial's Edit Action is in the form (say Form A):
[HttpPost]
public ActionResult Edit(int id, FormCollection collection) {
It seems to me that if you shape your ViewModels well to match your views, that the approach Form B:
[HttpPost]
public ActionResult Edit(MyViewModel mvm) {
just seems like a better, cleaner approach. I then just map the VM properties to the Model properties and save. However, if this ViewModel has other entities embedded in it that are initialized via the constructor (for example in the nerddinner tutorial), then this edit action fails if there is no default constructor and you'd have to use the first approach.
So, the first question is do you agree that generally Form B is usually better? Are there drawbacks?
Secondly, it seems then if Form B is used, the decorator type validation would need to be in the ViewModel. Are there advantages of embedding entities in ViewModels and keeping the validation at the entity level only?

This is a pretty general SO question.
the first question is do you agree that generally Form B is usually better?
The only time I do not use Form B is when I upload files. Otherwise, I don't believe anyone should ever need to use Form A. The reason I think people use Form A is a lack of understanding of the abilities of ASP.Net's version of MVC.
Secondly, it seems then if Form B is used, the decorator type validation would need to be in the ViewModel.
Sort of / it Depends. I'll give you an example:
public IValidateUserName
{
[Required]
string UserName { get; set; }
}
public UserModel
{
string UserName { get; set; }
}
[MetadataType(typeof(IValidateUserName))]
public UserValiationModel : UserModel
{
}
The validation decorator is in an interface. I'm using the MetadataType on a derived class to validate the derived type. I personally like this practice because it allows reusable validation and the MetadataType/Validation is NOT part of the ASP.NET core functionality, so it can be used outside of ASP.Net (MVC) application.
Are there advantages of embedding entities in ViewModels ..
Yes, I do my absolute best to never pass a basic model to the view. This is an example of what I don't do:
public class person { public Color FavoriteColor { get; set; } }
ActionResult Details()
{
Person model = new Person();
return this.View(model);
}
What happens when you want to pass more data to your view (for partials or layout data)? That information is not Person relevant most of the time so adding it to the Person model makes no sense. Instead, my models typically look like:
public class DetailsPersonViewModel()
{
public Person Person { get; set; }
}
public ActionResult Details()
{
DetailsPersonViewModel model = new DetailsPersonViewModel();
model.Person = new Person();
return this.View(model);
}
Now I can add required data the DetailsPersonViewModel that view needs beyond what a Person knows. For example, lets say this is going to display a for with all the colors for the Person to pick a favorite. All the possible colors aren't part of a person and shouldn't be part of the person Model, so I'd add them to the DetailPersonViewModel.
public class DetailsPersonViewModel()
{
public Person Person { get; set; }
public IEnumerable<Color> Colors { get; set; }
}
.. and keeping the validation at the entity level only?
System.ComponentModel.DataAnnotations weren't designed to validate properties' properties, so doing something like:
public class DetailsPersonViewModel()
{
[Required(property="FavoriteColor")]
public Person Person { get; set; }
}
Doesn't exist and doesn't make sense. Why ViewModel shouldn't contain the validation for the entity that needs validation.
this edit action fails if there is no default constructor and you'd have to use the first approach.
Correct, but why would a ViewModel or a Entity in a ViewModel not have a parameterless constructor? Sounds like a bad design and even if there is some requirement for this, it's easily solved by ModelBinding. Here's an example:
// Lets say that this person class requires
// a Guid for a constructor for some reason
public class Person
{
public Person(Guid id){ }
public FirstName { get; set; }
}
public class PersonEditViewModel
{
public Person Person { get; set; }
}
public ActionResult Edit()
{
PersonEditViewModel model = new PersonEditViewModel();
model.Person = new Person(guidFromSomeWhere);
return this.View(PersonEditViewModel);
}
//View
#Html.EditFor(m => m.Person.FirstName)
//Generated Html
<input type="Text" name="Person.FirstName" />
Now we have a form that a user can enter a new first name. How do we get back the values in this constructor? Simple, the ModelBinder does NOT care what model it is binding to, it just binds HTTP values to matching class properties.
[MetadataType(typeof(IPersonValidation))]
public class UpdatePerson
{
public FirstName { get; set; }
}
public class PersonUpdateViewModel
{
public UpdatePerson Person { get; set; }
}
[HttpPost]
public ActionResult Edit(PersonUpdateViewModel model)
{
// the model contains a .Person with a .FirstName of the input Text box
// the ModelBinder is simply populating the parameter with the values
// pass via Query, Forms, etc
// Validate Model
// AutoMap it or or whatever
// return a view
}

I have not yet taken a look at the NerDinner project, however, I generally try to avoid having a ViewModel in the POST of an action and instead, only have the elements of the "form" submitted.
For instance, if the ViewModel has a Dictionary that is used in some kind of dropdown, the entire dropdown will not be submitted, only the selected value.
My general approach is:
[HttpGet]
public ActionResult Edit(int id)
{
var form = _service.GetForm(id);
var pageViewModel = BuildViewModel(form);
return View(pageViewModel);
}
[HttpPost]
public ActionResult Edit(int id, MyCustomForm form)
{
var isSuccess = _service.ProcessForm(id);
if(isSuccess){
//redirect
}
//There was an error. Show the form again, but preserve the input values
var pageViewModel = BuildViewModel(form);
return View(pageViewModel);
}
private MyViewModel BuildViewModel(MyCustomForm form)
{
var viewModel = new MyViewModel();
viewModel.Form = form;
viewModel.StateList = _service.GetStateList();
return viewModel;
}

Related

Entering Value in Partial View and Posting it back to Main Controller in ASP.NET MVC 5 [duplicate]

I have a ViewModel that has a complex object as one of its members. The complex object has 4 properties (all strings). I'm trying to create a re-usable partial view where I can pass in the complex object and have it generate the html with html helpers for its properties. That's all working great. However, when I submit the form, the model binder isn't mapping the values back to the ViewModel's member so I don't get anything back on the server side. How can I read the values a user types into the html helpers for the complex object.
ViewModel
public class MyViewModel
{
public string SomeProperty { get; set; }
public MyComplexModel ComplexModel { get; set; }
}
MyComplexModel
public class MyComplexModel
{
public int id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
....
}
Controller
public class MyController : Controller
{
public ActionResult Index()
{
MyViewModel model = new MyViewModel();
model.ComplexModel = new MyComplexModel();
model.ComplexModel.id = 15;
return View(model);
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
// model here never has my nested model populated in the partial view
return View(model);
}
}
View
#using(Html.BeginForm("Index", "MyController", FormMethod.Post))
{
....
#Html.Partial("MyPartialView", Model.ComplexModel)
}
Partial View
#model my.path.to.namespace.MyComplexModel
#Html.TextBoxFor(m => m.Name)
...
how can I bind this data on form submission so that the parent model contains the data entered on the web form from the partial view?
thanks
EDIT: I've figured out that I need to prepend "ComplexModel." to all of my control's names in the partial view (textboxes) so that it maps to the nested object, but I can't pass the ViewModel type to the partial view to get that extra layer because it needs to be generic to accept several ViewModel types. I could just rewrite the name attribute with javascript, but that seems overly ghetto to me. How else can I do this?
EDIT 2: I can statically set the name attribute with new { Name="ComplexModel.Name" } so I think I'm in business unless someone has a better method?
You can pass the prefix to the partial using
#Html.Partial("MyPartialView", Model.ComplexModel,
new ViewDataDictionary { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "ComplexModel" }})
which will perpend the prefix to you controls name attribute so that <input name="Name" ../> will become <input name="ComplexModel.Name" ../> and correctly bind to typeof MyViewModel on post back
Edit
To make it a little easier, you can encapsulate this in a html helper
public static MvcHtmlString PartialFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, string partialViewName)
{
string name = ExpressionHelper.GetExpressionText(expression);
object model = ModelMetadata.FromLambdaExpression(expression, helper.ViewData).Model;
var viewData = new ViewDataDictionary(helper.ViewData)
{
TemplateInfo = new System.Web.Mvc.TemplateInfo
{
HtmlFieldPrefix = string.IsNullOrEmpty(helper.ViewData.TemplateInfo.HtmlFieldPrefix) ?
name : $"{helper.ViewData.TemplateInfo.HtmlFieldPrefix}.{name}"
}
};
return helper.Partial(partialViewName, model, viewData);
}
and use it as
#Html.PartialFor(m => m.ComplexModel, "MyPartialView")
If you use tag helpers, the partial tag helper accepts a for attribute, which does what you expect.
<partial name="MyPartialView" for="ComplexModel" />
Using the for attribute, rather than the typical model attribute, will cause all of the form fields within the partial to be named with the ComplexModel. prefix.
You can try passing the ViewModel to the partial.
#model my.path.to.namespace.MyViewModel
#Html.TextBoxFor(m => m.ComplexModel.Name)
Edit
You can create a base model and push the complex model in there and pass the based model to the partial.
public class MyViewModel :BaseModel
{
public string SomeProperty { get; set; }
}
public class MyViewModel2 :BaseModel
{
public string SomeProperty2 { get; set; }
}
public class BaseModel
{
public MyComplexModel ComplexModel { get; set; }
}
public class MyComplexModel
{
public int id { get; set; }
public string Name { get; set; }
...
}
Then your partial will be like below :
#model my.path.to.namespace.BaseModel
#Html.TextBoxFor(m => m.ComplexModel.Name)
If this is not an acceptable solution, you may have to think in terms of overriding the model binder. You can read about that here.
I came across the same situation and with the help of such informative posts changed my partial code to have prefix on generated in input elements generated by partial view
I have used Html.partial helper giving partialview name and object of ModelType and an instance of ViewDataDictionary object with Html Field Prefix to constructor of Html.partial.
This results in GET request of "xyz url" of "Main view" and rendering partial view inside it with input elements generated with prefix e.g. earlier Name="Title" now becomes Name="MySubType.Title" in respective HTML element and same for rest of the form input elements.
The problem occurred when POST request is made to "xyz url", expecting the Form which is filled in gets saved in to my database. But the MVC Modelbinder didn't bind my POSTed model data with form values filled in and also ModelState is also lost. The model in viewdata was also coming to null.
Finally I tried to update model data in Posted form using TryUppdateModel method which takes model instance and html prefix which was passed earlier to partial view,and can see now model is bound with values and model state is also present.
Please let me know if this approach is fine or bit diversified!

When to use BindAttribute?

I'm learning asp.net mvc and wonder when we need to use BindAttribute.
The first case (using Bind):
Model:
public class Book
{
public string Id { get; set; }
public string Name { get; set; }
public string Author { get; set; }
}
Controller:
public IActionResult Create([Bind(nameof(Book.Name), nameof(Book.Author))] Book model)
{
return Ok();
}
The book Id would be generated on server side. So, client side has nothing to do with it, every actions try to change/make the id is prevented.
The second case (not using Bind):
Model:
public class BookViewModel
{
public string Name { get; set; }
public string Author { get; set; }
}
Controller:
public IActionResult Create(BookViewModel model)
{
return Ok();
}
Because the second model doesn't contain Id property, we don't need to prevent from creating or changing.
I prefer the second. It's easy to manage model.
Is there a case we must use Bind attribute?
We use bind when we want that some properties of complex property are ignored when received on server. It could be for safety or other reasons.
When this action is executed the MVC model binder will use the request parameters to populate the user parameter's properties, as you may already know. However, the Bind attribute tells the model binder to only populate properties with names specified.
So in this case only the Username, FullName and Email properties will be populated. All others will be ignored.
See here for more details: http://ittecture.wordpress.com/2009/05/01/tip-of-the-day-199-asp-net-mvc-defining-model-binding-explicitly/
If you have situation when you only have to ignore one parametar from binding you could use Exclude property:
[Exclude]
public Entity Name {get; set;}
Bind is used to increase security and unauthorized data to be posted on server . In your model class , suppose you have content property also. if the content property is not needed in the future. Then it would be difficult for you to remove all the occurrences of that property. Here you can use bind property like this
[Bind(exclude="content")]
or you can bind only selected properties to be posted on server by including the properties like this
public ActionResult create([Bind(Include = "Name,Author")] Modelclass modelclass)
{
//Do something here
}
You can learn more about it here
Second approach is more suitable instead writing all the properties and bind them but there are some situations where you must bind user like you have a roles property or IsAdmin property in your model then you might not want that user somehow posts the IsAdmin or roles properties to the server . That's where you can use Bind attribute

ASP.NET MVC 3 Razor- Get data from custom editor

I have an experience in work with ASP.NET forms, but new to MVC.
How can I get data from shared views on postback?
In ASP.NET Forms I can write something like this:
ASP.NET Forms:
Model code:
public class MyModelItem
{
// Just TextBox is enough for editing this
public string SimpleProperty { get; set; }
// For this property separate NestedItemEditor.ascx is required
public MyModelNestedItem ComplexProperty { get; set; }
}
public class MyModelNestedItem
{
public string FirstProperty { get; set; }
public string SecondProperty { get; set; }
}
Behavior:
Control for editing MyModelNestedItem is separate ASCX control NestedItemEditor.ascx
This is just for example, MyModelNestedItem can be much more complex, I just want to give idea what I mean.
Now when I showing this item for editing, I'm showing one asp:TextBox and one NestedItemEditor.ascx. On page postback I'm gathering data from both and that's it.
Problem with MVC:
When I'm trying to implement this scenario with MVC, I'm using customized EditorFor (through using UIHint and creating shared view). So this shared view Views\Shared\EditorTemplates\MyModelNestedItem.cshtml can now display data that is already in MyModelNestedItem property but I have no idea how to make it return new entered data.
When parent controller recieves a post request, data seems to be in Request.Form, but which is civilized way to reach it? Sure, the best solution will be if data will fetch automatically into the MyModelItem.ComplexProperty.
The action which is called on post needs to be something like:
[HttpPost]
public ActionResult Index(MyViewModel mdl)
Then all the properties of the model which have input controls (or hidden inputs) on the form will have the data which was entered on the form (or passed to it or modified by javascript, in the case of hidden inputs).
This assumes that MyViewModel is the model referenced in your view.
Writing an ActionResult method in the controller with the complex type simply worked for me:
public class Topic
{
public Topic()
{
}
public DetailsClass Details
{
get;
set;
}
}
public class DetailsClass
{
public string TopicDetails
{
get;
set;
}
}
The view:
#modelTopic
#using (Html.BeginForm("Submit","Default"))
{
#Html.EditorFor(m=>m.Details)
#:<input type="submit" />
}
The controller:
public ActionResult Index()
{
Topic topic = new Topic();
return View( topic);
}
public ActionResult Submit(Topic t)
{
return View(t);
}
When submited, the Topic t contains the value i ented within the editor (Assuming You have a custom editor for the complex type, DetailsClass in my sample)

ASP.NET MVC3: can Action accept mulitple model parameters?

For example:
class MyContoller
{
[MyCustomAttribute]
public ActionResult MyAction(ModelX fromRequest, ModelY fromSession, ModelZ fromCookie)
{
string productId = fromRequest.ProductId;
string userId = fromSession.UserId;
string cultureName = fromCookie.CultureName;
}
}
Reason:
I don't want to visit Request, Session and HttpContext in the controllers, and the default idea of MVC3 which passing models to actions is very great.
I want the number of parameters of MyAction is easy to change. For example, if I add a new parameter, the system will try to look for values in Request, Session or Cookies by the name or type of the parameter (I think custom ModelBinders may be required for cookie values) and pass the filled model to my action. I don't have to write extra code.
Can the custom attribute (MyCustomAttribute in the example) accomplish this idea?
I am not sure I follow you about the custom attribute. What are you expecting the custom attribute to do?
Yes, an action method can take as many model parameters as you want. Obviously, only one can be bound in any given request (because a view can only have one model). Whichever one is found first will be bound, and the others will be null.
So let's say you have the following:
public class ModelX {
public string X {get;set;}
}
public class ModelY {
public string Y {get;set;}
}
public class ModelZ {
public string Z {get;set;}
}
And you have an action method like this:
public ActionResult DoIt(ModelX x, ModelY y, ModelZ z)
{
return View();
}
And in your DoIt.cshtml you have the following:
#model ModelZ
#using(Html.BeginForm()) {
#Html.TextBoxFor(x => x.Z)
<input type="submit"/>
}
If you type something into the textbox and submit, then the model binder will bind a ModelZ with the value you entered and ModelX and ModelY will be null.
If you mean can an action method bind multiple models simultaneously, then I would have to ask you.. How exactly do you plan to have a view have more than one model? You can certainly create a wrapper model to contain the multiple models, but a view can only have one.
Create a composite ViewModel class that incorporates ModelX, ModelY and ModelZ. You can then populate an instance of your new ViewModel class, and pass that to your controller method.
public class XYZViewModel
{
public ModelX fromRequest { get; set; }
public ModelY fromSession { get; set; }
public ModelZ fromCookie { get; set; }
}
public class MyController
{
[MyCustomAttribute]
public ActionResult MyAction(XYZViewModel myModel)
{
string productId = myModel.fromRequest.ProductId;
string userId = myModel.fromSession.UserId;
string cultureName = myModel.fromCookie.CultureName;
}
}
You can always pass multiple parameters to your controller action, yes. The key is to make sure they are properly serialized in the request. If you're using a form, that means using the Html helper methods.
For example, let's say you want an action like this:
public ActionResult Multiple(ModelA a, ModelB b)
{
// ...
}
You could create simple partial view for each model:
#model MyProject.Models.ModelA
#Html.EditorForModel()
Then in your Multiple view, render the partial views like so:
#{ using (Html.BeginForm("Multiple", "MyController", FormMethod.Get))
{
#Html.Partial("A", new MyProject.Models.ModelA())
#Html.Partial("B", new MyProject.Models.ModelB())
<input type='submit' value='submit' />
}
I set the method to GET here so that you can easily see how MVC passes the parameters. If you submit the form, you'll see that MVC successfully deserializes each object.

Modern way to handle and validate POST-data in MVC 2

There are a lot of articles devoted to working with data in MVC, and nothing about MVC 2.
So my question is: what is the proper way to handle POST-query and validate it.
Assume we have 2 actions. Both of them operates over the same entity, but each action has its own separated set of object properties that should be bound in automatic manner. For example:
Action "A" should bind only "Name" property of object, taken from POST-request
Action "B" should bind only "Date" property of object, taken from POST-request
As far as I understand - we cannot use Bind attribute in this case.
So - what are the best practices in MVC2 to handle POST-data and probably validate it?
UPD:
After Actions performed - additional logic will be applied to the objects so they become valid and ready to store in persistent layer. For action "A" - it will be setting up Date to current date.
I personally don't like using domain model classes as my view model. I find it causes problems with validation, formatting, and generally feels wrong. In fact, I'd not actually use a DateTime property on my view model at all (I'd format it as a string in my controller).
I would use two seperate view models, each with validation attributes, exposed as properties of your primary view model:
NOTE: I've left how to combining posted view-models with the main view model as an exercise for you, since there's several ways of approaching it
public class ActionAViewModel
{
[Required(ErrorMessage="Please enter your name")]
public string Name { get; set; }
}
public class ActionBViewModel
{
[Required(ErrorMessage="Please enter your date")]
// You could use a regex or custom attribute to do date validation,
// allowing you to have a custom error message for badly formatted
// dates
public string Date { get; set; }
}
public class PageViewModel
{
public ActionAViewModel ActionA { get; set; }
public ActionBViewModel ActionB { get; set; }
}
public class PageController
{
public ActionResult Index()
{
var viewModel = new PageViewModel
{
ActionA = new ActionAViewModel { Name = "Test" }
ActionB = new ActionBViewModel { Date = DateTime.Today.ToString(); }
};
return View(viewModel);
}
// The [Bind] prefix is there for when you use
// <%= Html.TextBoxFor(x => x.ActionA.Name) %>
public ActionResult ActionA(
[Bind(Prefix="ActionA")] ActionAViewModel viewModel)
{
if (ModelState.IsValid)
{
// Load model, update the Name, and commit the change
}
else
{
// Display Index with viewModel
// and default ActionBViewModel
}
}
public ActionResult ActionB(
[Bind(Prefix="ActionB")] ActionBViewModel viewModel)
{
if (ModelState.IsValid)
{
// Load model, update the Date, and commit the change
}
else
{
// Display Index with viewModel
// and default ActionAViewModel
}
}
}
One possible way to handle POST data and add validation, is with a custom model binder.
Here is a small sample of what i used recently to add custom validation to POST-form data :
public class Customer
{
public string Name { get; set; }
public DateTime Date { get; set; }
}
public class PageController : Controller
{
[HttpPost]
public ActionResult ActionA(Customer customer)
{
if(ModelState.IsValid) {
//do something with the customer
}
}
[HttpPost]
public ActionResult ActionB(Customer customer)
{
if(ModelState.IsValid) {
//do something with the customer
}
}
}
A CustomerModelBinder will be something like that:
public class CustomerModelBinder : DefaultModelBinder
{
protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor)
{
if (propertyDescriptor.Name == "Name") //or date or whatever else you want
{
//Access your Name property with valueprovider and do some magic before you bind it to the model.
//To add validation errors do (simple stuff)
if(string.IsNullOrEmpty(bindingContext.ValueProvider.GetValue("Name").AttemptedValue))
bindingContext.ModelState.AddModelError("Name", "Please enter a valid name");
//Any complex validation
}
else
{
//call the usual binder otherwise. I noticed that in this way you can use DataAnnotations as well.
base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
}
}
and in the global.asax put
ModelBinders.Binders.Add(typeof(Customer), new CustomerModelBinder());
If you want not to bind Name property (just Date) when you call ActionB, then just make one more custom Model Binder and in the "if" statement, put to return the null, or the already existing value, or whatever you want. Then in the controller put:
[HttpPost]
public ActionResult([ModelBinder(typeof(CustomerAModelBinder))] Customer customer)
[HttpPost]
public ActionResult([ModelBinder(typeof(CustomerBModelBinder))] Customer customer)
Where customerAmodelbinder will bind only name and customerBmodelbinder will bind only date.
This is the easiest way i have found, to validate model binding, and i have achieved some very cool results with complex view models. I bet there is something out there that i have missed, and maybe a more expert can answer.
Hope i got your question right...:)

Resources