Binding public fields with ASP.NET MVC as well as public properties? - c#

Obviously, ASP.NET MVC's binding functionality takes care of binding a model's public properties when passing it to a controller, so for instance in the following example, Surname and Email will be bound with their submitted values:
public ActionResult Create(UserModel mdlNewUser) {
// ...
}
// ...
public class UserModel {
public string Firstname;
public string Surname { get; set; }
public string Email { get; set; }
}
However, it doesn't seem to auto-bind public fields such as Firstname in the above example; these will be left untouched. Is there any way to get public fields (and for that matter, any other type of class member) to be automatically bound, or will it only ever bind public properties?
This article seems to imply that it's only public properties because it only ever refers to them all the way through, but it doesn't explicitly seem to say that only public properties will be bound.

That's correct, only public properties will be bound.
Fields cannot be used for binding.

Related

Asp.net core model doesn't bind from form

I catch post request from 3rd-side static page (generated by Adobe Muse) and handle it with MVC action.
<form method="post" enctype="multipart/form-data">
<input type="text" name="Name">
...
</form>
Routing for empty form action:
app.UseMvc(routes => routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}"));
But in according action I have model with every property is empty
Action:
[HttpPost]
public void Index(EmailModel email)
{
Debug.WriteLine("Sending email");
}
Model:
public class EmailModel
{
public string Name { get; set; }
public string Email { get; set; }
public string Company { get; set; }
public string Phone { get; set; }
public string Additional { get; set; }
}
Request.Form has all values from form, but model is empty
[0] {[Name, Example]}
[1] {[Email, Example#example.com]}
[2] {[Company, Hello]}
[3] {[Phone, Hello]}
[4] {[Additional, Hello]}
Be careful not to give an action parameter a name that is the same as a model property or the binder will attempt to bind to the parameter and fail.
public async Task<IActionResult> Index( EmailModel email ){ ... }
public class EmailModel{ public string Email { get; set; } }
Change the actions parameter 'email' to a different name and it will bind as expected.
public async Task<IActionResult> Index( EmailModel uniqueName ){ ... }
I'm not sure it is same case, but I had same problem and nothing really looks to work for me.
The problem in my case was that I had a property called Model in my view model class
public string Model { get; set; }
When I renamed the property to ModelName everything was working fine again, even without FromForm attribute.
Looks like some special property names could be a bit of a problem for asp.net mvc model binding.
So, my advice is to check your model properties and maybe try renaming them one by one in order to check if the problem is there.
Hope this helps.
ASP.Net Core 3.1
In my case, I was using a complex model (a model that contains other models, like a shared model) posted by Ajax, so the inputs in the view were automatically named like this "ChildModel.PropertyName" (see code)
[HttpPost]
[ValidateAntiForgeryToken] // ("AUVM.PropertyName")
public async Task<JsonResult> AddUser([FromForm]AUVM aUVM) //Parameter name must match the first part of the input name in order to bind
{
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<JsonResult> AddUser([FromForm]AUVM someUniqueName) //This is wrong and won't bind
{
}
I'm having the same problem
this docs helps me to understand Model Binding
https://docs.asp.net/en/latest/mvc/models/model-binding.html
I solved my problem by making sure that the property name is exact match in form field name
and I also add [FromForm] attribute to specify exactly the binding source.
I ran into this today, and though in hindsight it seems obvious, just thought I'd add that you need to make sure your access modifiers for the Properties on the model you're binding are correct. I had public MyProperty { get; internal set; } on some and they would not bind. Removed internal and they worked just fine.
Change void to ActionResult.
[HttpPost]
public ActionResult Index(EmailModel email)
And don't forget verifying AntiForgeryToken from your view and action.
// to your form in view
#Html.AntiForgeryToken()
// ------------
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(EmailModel email)
This issue can also happen if one or more of your properties in the request model fail to bind into an acceptable on the request model.
In my case, I was supposed to pass a List<string> type to a property, but accidentally passed a string. This resulted in the entire request model becoming null
In my case, I was using the MVC 4.0 convention of naming the object you are posting. E.g.,
js: $http.post("SaveAnswer", { answer: answerValues })
C#: public ActionResult SaveAnswer([FromBody] AnswerVM answer) {...}
When I changed the js to $http.post("SaveAnswer", answerValues), everything works.
I have run in to this issue where the model does not bind when I have more than one constructor on the underlying model like:
public class EmailModel
{
public EmailModel()
{}
public EmailModel(string _name, string _company)
{
Name = _name;
Company = _company;
}
public string Name { get; set; }
public string Email { get; set; }
public string Company { get; set; }
public string Phone { get; set; }
public string Additional { get; set; }
}
Fixed by removing all but one constructor like this:
public class EmailModel
{
public EmailModel()
{}
public string Name { get; set; }
public string Email { get; set; }
public string Company { get; set; }
public string Phone { get; set; }
public string Additional { get; set; }
}
I had the same problem and I want to share what happened in my case and how I got the solution. Perhaps, someone may find it useful to know.
My context was ASP.NET Core 3.1 Razor pages.
I had a handler as
public async Task<JsonResult> OnPostLogin([FromForm] LoginInput loginData)
{
}
and also I had a property in my ViewModel as
public LoginInput LoginInput { get; set; }
And in my Razor page, I have used the form like this:
<form asp-page-handler="Login" method="post">
<input asp-for="LoginData.UserNameOrEmail">
.....
So, notice that, in my form, I used the property LoginInput.UserNameOrEmail, but in my handler's parameter, I used the parameter name loginData. How will the ASP.NET Core know that, this LoginInput.UserNameOrEmail is actually loginData.UserNameOrEmail. It can't, right? Therefore, it did not bind my form data.
Then, when I renamed my ViewModel property "LoginInput" to the same name as the handler parameter, like this:
public LoginInput LoginData { get; set; }
The ASP.NET Core then found that the form data was matching the parameter name and then it started to bind properly. My problem was solved.

Submitting a model to action that is different from view model's type

I have two models:
public class PersonViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Title { get; set; }
}
public class DetailViewModel
{
public IEnumerable<string> Titles { get; set; }
public PersonViewModel Person { get; set; }
}
The form is presented with two fields, the first field being the Name, the second field being the a dropdown of Titles (Mr., Mrs. Miss., etc.)
The view for this page is strongly typed to DetailViewModel, and the Save method in the controller accepts a parameter of type PersonViewModel.
Since the view is strongly typed to a type that is different from the form action's parameter type, the names in the HttpRequest do not match what MVC is expecting in the action.
Is it possible to have MVC bind correctly with the model mismatch without having to manually specify form field names? (eg. I still want to use #Html.TextBoxFor(m => m.Person.Name))
For clarification, the form field names that are being submitted are similar to the following:
Person.Name=Matthew&Person.Title=Mr.
Where I need the following (for model binding to work):
Name=Matthew&Title=Mr.
You can use the Prefix property of BindAttribute in the action method
public ActionResult Edit([Bind(Prefix="Person")]PersonViewModel model)
{
}
This essentially strips the Person prefix from the property name while binding

ASP.NET MVC: Binding dynamically rendered partial views as nested properties of the view model

I have the following view models:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public BankAccount BankAccount { get; set; }
public PayPalAccount PayPalAccount { get; set; }
}
public class BankAccount
{
public string BankName { get; set; }
public string AccountNumber { get; set; }
}
public class PayPalAccount
{
public string PayPalEmail { get; set; }
}
In my single view I have a form that binds the Person model and also has a DropDownList that let's the user choose an account type.
Once the user makes his choice, I load dynamically using jQuery ajax one of the partial views that represent either the BankAccount or the PayPalAccount and I add it to the page.
After the user clicks submit, I call this action:
public ActionResult Save(Person person)
{
// Here I expect that either person.BankAccount or person.PayPalAccount
// will contain the user input, and the other will be null
}
How do I make the partial view properties bind to the nested property in my Person view model?
Your form fields must be prefixed (with BankAccount. or PayPalAccount.) so that they can be automatically bound.
When you return your partial view from Ajax, withthe current model you're using, their names don't have a prefix.
The easier way to prefix them is using exactly the same View Model class: Person for both partial views. If you do that, in the Razor you'll have to write
#Html.EditorFor(m => m.BankAccount.BankName)
The generated input field will have the required prefix BankAccount., i.e. it will look like this: <input ... name='BankAccount.BankName' ... />
You could also create a view model like this for your partial:
public class BakAccountModel
{
// make sure the property name is the same as in the Person view model
public BankAccount { get; set; }
}
An finally, you can try a solution like this:
ASP.NET MVC partial views: input name prefixes
I can't assure the last solution will work fine: for example it could break the ModelState. And is harder to implemente and "less standard".

Using the Bind attribute on a ViewModel class in ASP.NET MVC

Why might a developer use the Bind attribute on a ViewModel object in an ASP.NET MVC project and can this have a detrimental effect an application?
[Bind(Include = "Id,Name")]
[MetadataType(typeof (MyViewModelValidation))]
public class MyViewModel
{
public string CustomerProductUserName { get; set; }
[Display(Name = "Name")]
public string Name { get; set; }
}
public class MyViewModelValidation
{
[HiddenInput(DisplayValue = false)]
public int Id { get; set; }
[Required]
public string Name{ get; set; }
}
First of all, you don't need to create a MetadataType class for a ViewModel. You can use data annotation attributes directly in your ViewModel. MetadataType classes are used for Models automatically generated by EF or other ORMs, so that you can use data annotation attributes without touching the auto-generated code.
The Bind attribute does not have to be used either - unless you want to use Include or Exclude properties of the Bind attribute, to include or exclude properties in your Model in or from binding, respectively.
For example, in the code in your question, only the Id and Name properties will be bound when submitting your Model from your View. Even if you have an input in your View for CustomerProductUserName, when you submit your form, the property will always be null. This can be useful in cases like where you don't want an auto-generated ID field to be included in binding.
Properties excluded from binding are also excluded from validation, because validation is done as part of model binding. Also, you may use the Bind attribute for security reasons; for instance, when you want to make sure nothing but the properties in your model are being posted to the controller.
The purpose of using bind attribute is to prevent attacker from assigning property value while posting of request or control what properties you want to bind.
Let us suppose, you have a class called Member and a create method that saves member. But you do not want user to send a value for MemberType property.
Class Member
{
public int MemberId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string MemberType { get; set; }
}
[HttpPost]
Public ActionResult Create(Member member)
{
Save(member);
}
Let's say for now, you are only offering Regular member type which is the default value. You might think that you can prevent the user to send a value for MemberType property by not allowing input for MemberType Property. But when a user posts the member object, an attacker may intercept the request and send the MemberType value in request, as
MemberId=1&FirstName=Chandra&LastName=Malla&MemberType=Premium and save the member as a Premium member. To prevent this, you can decorate Member class with Bind attribute.
[Bind(Include="MemberId,FirstName,LastName")]
Class Member
{
...
or
[Bind(Exclude="MemberType")]
Class Member
{
...
Now if Member object is posted, MemberType property value will not be posted.
If you are using ViewModel, you might not necessarily have to use bind attribute because you can omit MemberType properties in your ViewModel.
Class Member
{
public int MemberId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string MemberType { get; set; }
}
Class MemberViewModel
{
public int MemberId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
[HttpPost]
Public ActionResult Create(MemberViewModel memberviewmodel)
{
Save(memberviewmodel);
}
If you do not nicely design your model and/or ViewModel and do not use bind attribute to avoid posting of property you do not want, that might have detrimental effect.
In addition, if you want to prevent refactoring issues when renaming properties from your model you could do something like:
public async Task<ActionResult> Create([Bind(Include = nameof(Foo.Bar1)+","+nameof(Foo.Bar2)+","+nameof(Foo.Bar3))] Foo fooObj)
If you now e.g. rename "Bar1" your Include Bindings will still work.
You can use the Bind attribute to control how a model binder converts a request into an
object. The most common way that you use the Bind attribute is when you exclude an Id property from binding. For example, the Persons database table includes a column named Id
that is an Identity column. Because the value of an Identity column is generated by the
database automatically, you don’t want to bind a form field to this property.
On the other hand, imagine that a property of a model is especially sensitive, which a malicious user could simply append it in a URL when submitting a form. If this were done, the model binder would happily discover and use the data value in the binding process. By Bind attribute you can protect your application from this kind of attack.
Using the Bind attribute could make problem(s) when you, for example, are going to update an entity and the ID is important for you.

How to "DRY up" C# attributes in Models and ViewModels?

This question was inspired by my struggles with ASP.NET MVC, but I think it applies to other situations as well.
Let's say I have an ORM-generated Model and two ViewModels (one for a "details" view and one for an "edit" view):
Model
public class FooModel // ORM generated
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmailAddress { get; set; }
public int Age { get; set; }
public int CategoryId { get; set; }
}
Display ViewModel
public class FooDisplayViewModel // use for "details" view
{
[DisplayName("ID Number")]
public int Id { get; set; }
[DisplayName("First Name")]
public string FirstName { get; set; }
[DisplayName("Last Name")]
public string LastName { get; set; }
[DisplayName("Email Address")]
[DataType("EmailAddress")]
public string EmailAddress { get; set; }
public int Age { get; set; }
[DisplayName("Category")]
public string CategoryName { get; set; }
}
Edit ViewModel
public class FooEditViewModel // use for "edit" view
{
[DisplayName("First Name")] // not DRY
public string FirstName { get; set; }
[DisplayName("Last Name")] // not DRY
public string LastName { get; set; }
[DisplayName("Email Address")] // not DRY
[DataType("EmailAddress")] // not DRY
public string EmailAddress { get; set; }
public int Age { get; set; }
[DisplayName("Category")] // not DRY
public SelectList Categories { get; set; }
}
Note that the attributes on the ViewModels are not DRY--a lot of information is repeated. Now imagine this scenario multiplied by 10 or 100, and you can see that it can quickly become quite tedious and error prone to ensure consistency across ViewModels (and therefore across Views).
How can I "DRY up" this code?
Before you answer, "Just put all the attributes on FooModel," I've tried that, but it didn't work because I need to keep my ViewModels "flat". In other words, I can't just compose each ViewModel with a Model--I need my ViewModel to have only the properties (and attributes) that should be consumed by the View, and the View can't burrow into sub-properties to get at the values.
Update
LukLed's answer suggests using inheritance. This definitely reduces the amount of non-DRY code, but it doesn't eliminate it. Note that, in my example above, the DisplayName attribute for the Category property would need to be written twice because the data type of the property is different between the display and edit ViewModels. This isn't going to be a big deal on a small scale, but as the size and complexity of a project scales up (imagine a lot more properties, more attributes per property, more views per model), there is still the potentially for "repeating yourself" a fair amount. Perhaps I'm taking DRY too far here, but I'd still rather have all my "friendly names", data types, validation rules, etc. typed out only once.
I'll assume that your doing this to take advantage of the HtmlHelpers EditorFor and DisplayFor and don't want the overhead of ceremoniously declaring the same thing 4000 times throughout the application.
The easiest way to DRY this up is to implement your own ModelMetadataProvider. The ModelMetadataProvider is what is reading those attributes and presenting them to the template helpers. MVC2 already provides a DataAnnotationsModelMetadataProvider implementation to get things going so inheriting from that makes things really easy.
To get you started here is a simple example that breaks apart camelcased property names into spaces, FirstName => First Name :
public class ConventionModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
var metadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
HumanizePropertyNamesAsDisplayName(metadata);
if (metadata.DisplayName.ToUpper() == "ID")
metadata.DisplayName = "Id Number";
return metadata;
}
private void HumanizePropertyNamesAsDisplayName(ModelMetadata metadata)
{
metadata.DisplayName = HumanizeCamel((metadata.DisplayName ?? metadata.PropertyName));
}
public static string HumanizeCamel(string camelCasedString)
{
if (camelCasedString == null)
return "";
StringBuilder sb = new StringBuilder();
char last = char.MinValue;
foreach (char c in camelCasedString)
{
if (char.IsLower(last) && char.IsUpper(c))
{
sb.Append(' ');
}
sb.Append(c);
last = c;
}
return sb.ToString();
}
}
Then all you have to do is register it like adding your own custom ViewEngine or ControllerFactory inside of Global.asax's Application Start:
ModelMetadataProviders.Current = new ConventionModelMetadataProvider();
Now just to show you I'm not cheating this is the view model I'm using to get the same HtmlHelper.*.For experience as your decorated ViewModel:
public class FooDisplayViewModel // use for "details" view
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[DataType("EmailAddress")]
public string EmailAddress { get; set; }
public int Age { get; set; }
[DisplayName("Category")]
public string CategoryName { get; set; }
}
Declare BaseModel, inherit and add another properties:
public class BaseFooViewModel
{
[DisplayName("First Name")]
public string FirstName { get; set; }
[DisplayName("Last Name")]
public string LastName { get; set; }
[DisplayName("Email Address")]
[DataType("EmailAddress")]
public string EmailAddress { get; set; }
}
public class FooDisplayViewModel : BaseFooViewModel
{
[DisplayName("ID Number")]
public int Id { get; set; }
}
public class FooEditViewModel : BaseFooViewModel
EDIT
About categories. Shouldn't edit view model have public string CategoryName { get; set; } and public List<string> Categories { get; set; } instead of SelectList? This way you can place public string CategoryName { get; set; } in base class and keep DRY. Edit view enhances class by adding List<string>.
As LukLed said you could create a base class that the View and Edit models derive from, or you could also just derive one view model from the other. In many apps the Edit model is basically the same as View plus some additional stuff (like select lists), so it might make sense to derive the Edit model from the View model.
Or, if you're worried about "class explosion", you could use the same view model for both and pass the additional stuff (like SelectLists) through ViewData. I don't recommend this approach because I think it's confusing to pass some state via the Model and other state via ViewData, but it's an option.
Another option would be to just embrace the separate models. I'm all about keeping logic DRY, but I'm less worried about a few redundant properties in my DTOs (especially on projects using code generation to generate 90% of the view models for me).
First thing i notice - you got 2 view models. See my answer here for details on this.
Other things that springs in mind are already mentioned (classic approach to apply DRY - inheritance and conventions).
I guess i was too vague. My idea is to create view model per domain model and then - combine them at view models that are per specific view. In your case: =>
public class FooViewModel {
strange attributes everywhere tralalala
firstname,lastname,bar,fizz,buzz
}
public class FooDetailsViewModel {
public FooViewModel Foo {get;set;}
some additional bull**** if needed
}
public class FooEditViewModel {
public FooViewModel Foo {get;set;}
some additional bull**** if needed
}
That allows us to create more complex view models (that are per view) too =>
public class ComplexViewModel {
public PaginationInfo Pagination {get;set;}
public FooViewModel Foo {get;set;}
public BarViewModel Bar {get;set;}
public HttpContext lol {get;set;}
}
You might find useful this question of mine.
hmm... turns out i actually did suggest to create 3 view models. Anyway, that code snippet kind a reflects my approach.
Another tip - i would go with filter & convention (e.g. by type) based mechanism that fills viewdata with necessary selectList (mvc framework can automagically bind selectList from viewData by name or something).
And another tip - if you use AutoMapper for managing your view model, it has nice feature - it can flatten object graph. Therefore - you can create view model (which is per view) that directly has props of view model (which is per domain model) whatever how much deep you want to go (Haack said it's fine).
These display names (the values) could perhaps be displayed in another static class with a lot of const fields. Wouldn't save you having many instances of DisplayNameAttribute but it would make a name change quick and easy to make. Obviously this is isn't helpful for other meta attributes.
If I told my team they would have to create a new model for every little permutation of the same data (and subsequently write automapper definitions for them) they'd revolt and lynch me. I'd rather model metadata that was, too some degree use aware. For example making a properties required attribute only take effect in an "Add" (Model == null) scenario. Particularly as I wouldn't even write two views to handle add/edit. I would have one view to handle the both of them and if I started having different model classes I'd get into trouble with my parent class declaration.. the ...ViewPage bit.

Categories

Resources