ASP.NET MVC Model Binder Fails with Strings that look like version numbers? - asp.net-mvc

This should be straight-forward, but here goes - we're using MVC4 to handle a multi-part Form request to upload a binary along with some meta-data via a strongly-typed view in MVC4.
One of the fields is a version number for the file (i.e. something like 0.0.6, 0.4.5-pre, etc...)
I'm getting the following error from the model binder when it tries to bind this version number field to the model field (string type):
{"The parameter conversion from type 'System.String' to type
'Models.NewFileVersion' failed because no type converter can convert
between these types."}
Specifically the error can be traced to our "VersionNumber" field - any ideas as to why this might be happening?
Edit: Source code below
NewFileVersion.cs
public class NewFileVersion
{
[Display(Name = "Version # (0.67, 0.66-pre, etc...)")]
[Required]
public string Version { get; set; }
[Required]
[StringLength(2000, ErrorMessage = "ChangeLog must be between 30 an 2000 characters", MinimumLength = 30)]
[Display(Name = "Version Notes (will be visible to end-users)")]
[DataType(DataType.MultilineText)]
public string ChangeLog { get; set; }
[Display(Name = "Target Platform")]
[UIHint("Enum")]
public FileType PlatformTarget { get; set; }
}
New.cshtml
#model ViewModels.NewFileVersion
#{
ViewBag.Title = "New";
}
<div class="container" id="main-content">
<div class="row">
<h2>
New</h2>
#using (Html.BeginForm("Create", "Files", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>NewFileVersion</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Version)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Version)
#Html.ValidationMessageFor(model => model.Version)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ChangeLog)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ChangeLog)
#Html.ValidationMessageFor(model => model.ChangeLog)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.PlatformTarget)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.PlatformTarget)
</div>
<div class="editor-label">
<label for="">
File:</label></div>
<div class="editor-field">
<input type="file" name="fileData" required="required" /></div>
<p>
<input type="submit" value="Upload File" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
</div>
</div>
FilesController.cs
[HttpPost]
public ActionResult Create(NewFileVersion version, HttpPostedFileBase fileData)
{
//if our model is valid
if(ModelState.IsValid)
{
//etc....
}
ModelState.AddModelError("", "Invalid file submission");
return View("New", version);
}

Try renaming the version parameter for your Create action, e.g:
public ActionResult Create(NewFileVersion fileVersion, HttpPostedFileBase fileData) { ... }
The model binder may be getting confused between the string version model property and the NewFileVersion version action parameter.
You can see why this happens in the BindModel method, because the model has a property exactly matching the name of the action parameter it tries to bind as a simple type/model rather than a complex one.

Related

IValidatableObject.Validate does not fire if DataAnnoations add ValidationResult

With a standard ASP.NET MVC controller and view and a model that both implements IValidatableObject and has DataAnnotations, the Validate method never fires if the DataAnnotations generate an exception.
Here's the model...
public class ModelStaticDA : IValidatableObject {
public long Id { get; set; }
[EmailAddress]
public string EmailAddress { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
yield return new ValidationResult("MODEL NOT VALID!")
}
}
Here's the view (client validation is disabled for this demo)...
#model BindingAndValidation.Models.ModelStaticDA
#{
ViewBag.Title = "Create";
HtmlHelper.ClientValidationEnabled = false;
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>ModelStaticDA</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.EmailAddress, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBoxFor(model => model.EmailAddress)
#Html.ValidationMessageFor(model => model.EmailAddress)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
If you post something like "invalid" to EmailAddress, only the DataAnnotation message displays. If you post a valid e-mail address, the message from Validate displays.
Is this the correct behavior? If so, why? If not, what am I doing wrong?
You are doing everything right, that's the behavior. My guess it was designed this way to avoid having to validate again while working with the properties inside the Validate method, you know that when it's called you are working with valid data, and you can do things that require valid data.

MVC4 many types of validation

i have this view
#using (Html.BeginForm("RegisterApartmentOwner", "Home", FormMethod.Post,
new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<div class="editor-label">
#Html.LabelFor(model => model.FirstName)
</div>
<div class="editor-field">
#Html.TextBoxFor(x => x.FirstName, new {placeholder = "Enter Your First Name" })
#Html.ValidationMessageFor(model => model.FirstName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.LastName)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.LastName, new { placeholder = "Enter Your Last Name"})
#Html.ValidationMessageFor(model => model.LastName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Password)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.Password, new { placeholder = "Enter Your Password"})
#Html.ValidationMessageFor(model => model.Password)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Password)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.Password, new { placeholder = "Enter Your Password Again"})
#Html.ValidationMessageFor(model => model.Password)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.MobileNumber)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.MobileNumber, new { placeholder = "Enter Your Mobile Number"})
#Html.ValidationMessageFor(model => model.MobileNumber)
</div>
<input type="submit" value="Register" class="submit"/>
}
my problem is that the validation works just when the fields are empty, but i want the validation to discover when the two passwords are not equal, and when the mobilenumber is not numbers and so on. what should i do please?
You should take a look at data annotations http://msdn.microsoft.com/en-us/library/dd901590(v=vs.95).aspx
You could try the jQuery.Validation.Unobtrusive.Native nuget package. It's really easy to implement and will fit your needs.
Installation
Just add to you web.config file
<appSettings>
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
</appSettings>
and in your Visual Studio go to Tools -> Library Package Manager -> Package Manager Console
Now in the console type
PM> Install-Package jQuery.Validation.Unobtrusive.Native.MVC4
After you installed the package you should check out the demo site or download the
source from github for more information
jQuery.Validation.Unobtrusive.Native Package
jQuery.Validation.Unobtrusive.Native.Demo Site
Using NuGet Package Manager
And in your case look at this Examples:
EqualTo
Demo Validation
Best regards
you can use the Data Annotations Extensions library it is available as nuget pagkage
Here is the site http://dataannotationsextensions.org/
Nuget package http://www.nuget.org/packages/DataAnnotationsExtensions
Demos http://dataannotationsextensions.org/Home/Demos
For the model you have created you can use data annotations and as per #kkern if u have enabled unobtrusive validation enabled and included all js file reference you can validate them by simply adding attributes in your properties. A Sample includes as following:
public class MyModel
{
[Required(ErrorMessage="First Name is Required")]
public string FirstName {get; set;}
[Required(ErrorMessage="")]
public string Password {get; set;}
[Compare("Password")]
public string ConfirmPassword {get; set;}
[RegularExpression(#"^[0-9]{7,13}$", ErrorMessage = "Contact No must be digits only and length of 7-13.")]
public string MobileNo {get; set;}
}
ASP.NET MVC has some validation attributes like RegularExpression or DataType but they are not enough for some situations. In that case you need a mask for your textbox and the best one is meio. The site provides you lots of examples for different cases.
To check the equality of two passwords you need to write some javascript code or add a new rule in Jquery validation. Comparing two passwords with Jquery might be helpful. The Compare attribute is another choice to compare two values.
Use could use in your model a self-validating approach:
public class TestModel : IValidatableObject
{
public string FirstName { get; set; }
public string Password { get; set; }
public string PasswordConfirm { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (FirstName == null)
{
yield return new ValidationResult("FirstName is mandatory.");
}
if (Password != PasswordConfirm)
{
yield return new ValidationResult("Password confirmation does not match.");
}
}
}
In your controller you would have something like this:
[HttpPost]
public ActionResult Create(Model model) {
if (!ModelState.IsValid) {
var errors = model.Validate(new ValidationContext(model, null, null));
foreach (var error in errors)
{
foreach (var memberName in error.MemberNames)
{
ModelState.AddModelError(memberName, error.ErrorMessage);
}
}
return View(model);
}
}
More info about this approach: How to force MVC to Validate IValidatableObject

HttpPostedFileBase not binding to model

here is my ViewModel
public class FaultTypeViewModel
{
[HiddenInput(DisplayValue = false)]
public int TypeID { get; set; }
[Required(ErrorMessageResourceType = typeof(AdministrationStrings), ErrorMessageResourceName = "FaultTypeNameRequired")]
[Display(ResourceType = typeof(AdministrationStrings), Name = "FaultTypeName")]
public string TypeName { get; set; }
[Display(ResourceType = typeof(AdministrationStrings), Name = "FaultTypeDescription")]
[DataType(DataType.MultilineText)]
public string TypeDescription { get; set; }
[Display(ResourceType = typeof(AdministrationStrings), Name = "FaultTypeImageFile")]
public HttpPostedFileBase TypeImageFile { get; set; }
[HiddenInput(DisplayValue = false)]
public string TypeImageURL { get; set; }
}
Notice I have a "TypeImageFile" HttpPostedFileBase
I would expect that the model binder would bond that property from the form to the model passes to the controller bu I just keep receiving null.
here is the relevant code in the View:
#using (Html.BeginForm("AddFaultType","Administration", FormMethod.Post))
{
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">
×</button>
<h3 id="myModalLabel">#SharedStrings.Add #SharedStrings.FaultType</h3>
</div>
<div class="modal-body">
#Html.ValidationSummary(true)
<div class="editor-label">
#Html.LabelFor(model => model.TypeName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.TypeName)
#Html.ValidationMessageFor(model => model.TypeName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.TypeDescription)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.TypeDescription)
#Html.ValidationMessageFor(model => model.TypeDescription)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.TypeImageFile)
</div>
<div class="editor-field">
<input type="file" name="TypeImageFile" id="TypeImageFile" />
</div>
</div>
<div class="modal-footer">
<input type="submit" value="#SharedStrings.Add" class="btn btn-primary" />
#Html.ActionLink(SharedStrings.Cancel, "Index", "Administration", null, new { Class = "btn", data_dismiss = "modal", aria_hidden = "true" })
</div>
}
and here is the controller:
[HttpPost]
public ActionResult AddFaultType(FaultTypeViewModel i_FaultToAdd)
{
var fileName = Path.GetFileName(i_FaultToAdd.TypeImageFile.FileName);
var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName);
i_FaultToAdd.TypeImageFile.SaveAs(path);
return RedirectToAction("Index");
}
Make sure you've set the enctype attribute on your form to multipart/form-data on your form if you want to be able to upload files:
#using (Html.BeginForm("AddFaultType", "Administration", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
...
}
Completing Darin's answer:
Make sure you've set the enctype attribute on your form to multipart/form-data on your form if you want to be able to upload files:
#using (Html.BeginForm("AddFaultType", "Administration", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
...
}
To ensure your <input> is transmitted to the controller as part of the model use the Html Helpers for Id and name like below:
<input type="file" id="#Html.IdFor(x=>x.HttpPostedFileBase)" name="#Html.NameFor(x=>x.HttpPostedFileBase)" accept=".csv,.txt"/>
Works in MVC5 sorry I cant find any reference to which helpers are available in MVC3

MVC3 - How to add dropdown to a form (Post) populated with different entity

Hi I am working in a MVC 3 application. I have a Create Form with following code.
#model Xrm.Student
#{
ViewBag.Title = "Create Student Record";
}
#using (Html.BeginForm("Create", "Student", FormMethod.Post))
{
<div class="editor-label">
#Html.LabelFor(model => #Model.FirstName)
</div>
<div class="editor-field">
#Html.EditorFor(model => #Model.FirstName)
#Html.ValidationMessageFor(model => #Model.FirstName)
</div>
<div>
<input id="Submit1" type="submit" value="Submit" />
</div>
}
I want to add a new drop down under Firsname field which should be populated with pubjects. Subject is different Entity. I could be very easy, but I am newbie with MVC so I just stuck here. Can anyone please suggest me the way to achieve it.
Thanks and Regards
I would define a view model:
public class MyViewModel
{
public Student Student { get; set; }
[DisplayName("Subject")]
[Required]
public string SubjectId { get; set; }
public IEnumerable<Subject> Subjects { get; set; }
}
and then have your controller populate and pass this view model to the view:
public ActionResult Create()
{
var model = new MyViewModel();
model.Student = new Student();
model.Subjects = db.Subjects;
return View(model);
}
and finally have your view strongly typed to the view model:
#model MyViewModel
#{
ViewBag.Title = "Create Student Record";
}
#using (Html.BeginForm())
{
<div class="editor-label">
#Html.LabelFor(x => x.Student.FirstName)
</div>
<div class="editor-field">
#Html.EditorFor(x => x.Student.FirstName)
#Html.ValidationMessageFor(x => x.Student.FirstName)
</div>
<div class="editor-label">
#Html.LabelFor(x => x.SubjectId)
</div>
<div class="editor-field">
#Html.DropDownListFor(
x => x.SubjectId,
new SelectList(Model.Subjects, "Id", "Name"),
"-- Subject --"
)
#Html.ValidationMessageFor(x => x.SubjectId)
</div>
<div>
<input type="submit" value="Submit" />
</div>
}
The "Id" and "Name" values I used for the SelectList must obviously be existing properties on your Subject class that you want to be used as respectively binding the id and the text of each option of the dropdown.

DateTime required in ModelState, however never set to be

I have a DateTime field in my form. For whatever reason, everytime I submit the form ModelState comes back as invalid and a message that my date time field (called PostDate) is required, even though in the view model I don't have the required attribute set.
Does anyone know why this would be happening as I'm going around in circles trying to figure it out.
Here is the view model
public class BlogViewModel
{
[Required]
public string Title { get; set; }
[Required]
public string Content { get; set; }
public bool Published { get; set; }
public DateTime PostDate { get; set; }
}
Here is the controller actions
public ActionResult Create()
{
return View();
}
//
// POST: /Admin/Blog/Create
[HttpPost]
[ValidateInput(false)]
public ActionResult Create(BlogViewModel model)
{
if(ModelState.IsValid)
{
model.Content = HtmlSanitizer.SanitizeHtml(model.Content);
Services.BlogService.CreateBlogPost(model.Title, model.Content, User.Identity.Name);
return RedirectToAction("Index");
}
return View(model);
}
Here is the View
#using Payntbrush.Infrastructure.Mvc.Extensions
#model Payntbrush.Presentation.Demo.MVC3.Areas.Admin.Models.BlogViewModel
#Html.Resource(Html.ScriptTag("Areas/Admin/js/plugins/wysiwyg/jquery.wysiwyg.js"), ResourceType.Js)
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm((string)ViewBag.Action, "Blog", FormMethod.Post, new { Id = "BlogEditor" }))
{
#Html.ValidationSummary(true)
<div class="editor-label">
#Html.LabelFor(model => model.Title)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Title)
#Html.ValidationMessageFor(model => model.Title)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Content)
</div>
<div class="editor-field">
#Html.TextAreaFor(model => model.Content, new { #class = "wysiwyg blog-editor" })
#Html.ValidationMessageFor(model => model.Content)
</div>
<div class="editor-label">
Time of post (Only set this if you want to make a post appear to have been published at a different date.)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.PostDate, new{#class="datetimepicker"})
#Html.ValidationMessageFor(model => model.PostDate)
</div>
<div class="editor-label">
Published (Check to make this post live on the site)
</div>
<div class="editor-field">
#Html.CheckBoxFor(model => model.Published)
#Html.ValidationMessageFor(model => model.Published)
</div>
<p>
<input class="large awesome" type="submit" value="Create" />
#Html.ActionLink("Cancel", "Index", "Blog", null, new { #class = "large awesome cancel-button" })
</p>
}
<div>
</div>
If you leave the corresponding field empty your model will be invalid because DateTime is a value type. You should use a nullable DateTime if you want to allow empty values:
public DateTime? PostDate { get; set; }

Resources