IFormfile in a view model is not binding - asp.net-mvc-4

I am using a viewmodel in .net standard project in my asp.net 4.72 framework mvc application and I have a file upload functionality in the app. IFormfile is always null in the controller method. Below are my model, view and controller method.
Model in .Net core
public class CustomerImportForm
{
public CustomerImportForm();
[Required]
public int CohortId { get; set; }
public ImporterType ImporterType { get; set; }
[Required]
public IFormFile ImportFile { get; set; }
}
View in Asp.net 4.7
#model CustomerImportForm
<div class="form-group">
#Html.LabelFor(m => m.ImportFile)
<div class="input-group">
<div class="custom-file">
<input type="file" class="custom-file-input"
id="#Html.IdFor(m => m.ImportFile)"
name="#Html.NameFor(m => m.ImportFile)"
accept=".xlsx,.csv"
required/>
<label class="custom-file-label" for="#Html.IdFor(m => m.ImportFile)">Choose file</label>
</div>
</div>
#Html.ValidationMessageFor(m => m.ImportFile, null, new {#class = "text-danger" })
</div>
Controller Method:
public async Task<ActionResult> ProcessUploadAndCreateCohorts(CustomerImportForm form)
{
Log.Info("Uploading import '{0}'", form.ImportFile.FileName);
return RedirectToAction("View", new { id = importId });
}
The form.ImportFile is always null.

Related

Why this Model Binding not working in Razor Page

I am using ASP.NET Core 3.1 with a simple example to test model binding to post a form. The property to be bound is an object named "Student". Bud the model binding is not working with post method. I will appreciate any help to point out what's wrong here.
Here are the code of my test programs:
'Student Class':
namespace ModelBindPost.Models
{
public class Student
{
public int Id;
public string FirstName;
public string LastName;
}
}
'Edit.cshtml.cs'
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using ModelBindPost.Models;
namespace ModelBindPost.Pages
{
public class EditModel : PageModel
{
[BindProperty(SupportsGet = true)]
public Student Student { get; set; }
public EditModel()
{
Student = new Student();
}
public IActionResult OnGet()
{
Student.Id = 1;
Student.FirstName = "Jean";
Student.LastName = "Smith";
return Page();
}
public IActionResult OnPost()
{
string name = this.Student.FirstName;
return Page();
}
}
}
' Edit.cshtml':
#page
#model ModelBindPost.Pages.EditModel
#{
}
<h2>Model Binding Test</h2>
<form method="post">
<div class="form-group">
<lable asp-for="Student.Id"></lable>
<input asp-for="Student.Id" class="form-control" />
</div>
<div class="form-group">
<lable asp-for="Student.FirstName"></lable>
<input asp-for="Student.FirstName" class="form-control" />
</div>
<div class="form-group">
<lable asp-for="Student.LastName"></lable>
<input asp-for="Student.LastName" class="form-control" />
</div>
<button type="submit" class="btn btn-primary">Save</button>
</form>
A simple public field cannot work for model binding. You need add getter and setter to create property like below:
public class Student
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}

How to solve empty model trouble using both Ajax.BeginForm and Html.BeginForm in MVC

I am new to MVC and stuck in passing modal to controller.
I have read many similar threads in SO, to no avail.
Here, I have a view for entering order details.
User will enter order item details (using ajax.BeginForm) and when he clicks save, whole order will be saved in backend (using Html.BeginForm). Ajax.BeginForm is working properly and passing + displaying records properly. But Html.BeginForm is passing model as nothing.
Here is my code ...
My Models
public class OrderItemsModel
{
public string SrNo { get; set; }
public int? ItemCode { get; set; }
public string ItemName { get; set; }
public Decimal? Qty { get; set; }
public Decimal? Rate { get; set; }
public Decimal? Amount { get; set; }
}
public class OrderModel
{
public string OrderNumber { get; set; }
public string OrderDate { get; set; }
public int? CustomerCode { get; set; }
public string CustomerName { get; set; }
public string Note { get; set; }
//List of items selected in Order
public List<OrderItemsModel> ItemsSelected { get; set; }
}
Extract from My View
#model OrderApplication.Models.OrderModel
#{
ViewBag.Title = "Index";
Model.ItemsSelected = ViewBag.getlist;
}
#using (Ajax.BeginForm("UpdateItemList", "Order", new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "selectedtable" }))
{
<h2 style="margin-left:5%;">Order Entry</h2>
//Order No & Date
<div class="row">
<div class="col-sm-6">
<div class="col-sm-3">
#Html.LabelFor(model => model.OrderNumber, "OrderNo"):
</div>
<div class="col-sm-3">
#Html.TextBoxFor(model => model.OrderNumber, new { #class = "form-control", #readonly = "readonly" })
</div>
</div>
<div class="col-sm-6">
<div class="col-sm-3">
#Html.LabelFor(model => model.OrderDate, "Date"):
</div>
<div class="col-sm-3">
#Html.TextBoxFor(model => model.OrderDate, new { #class = "form-control" })
</div>
</div>
</div>
<br />
//Table of entries
<div id="selectedtable">
#Html.Partial("_selectTable", Model);
</div>
<br />
}
#*Main Save*#
<div class="row">
<div class="col-sm-12">
<div class="col-sm-3">
#using (Html.BeginForm("SaveData", "Order", new { order = Model, id = "loginform", #class = "justify-content-center" }))
{
<input type="submit" value="Save Order" class="btn btn-success" />
}
</div>
<div class="col-sm-3">
<input type="button" class="btn btn-success" value="Clear Form" onclick="location.href='#Url.Action("Clear", "Order")'" />
</div>
</div>
</div>
My Controller
public class OrderController : Controller
{
public List<OrderItemsModel> dd = new List<OrderItemsModel>() ;
[HttpPost]
public ActionResult SaveData(OrderModel order, string id)
{
if (order == null) //order is always Nothing
{
return View(order);
}
if (order.CustomerCode == 0)
{
return View(order);
}
return View(order);
}
}
}
You shouldn't use both Ajax.BeginForm and Html.BeginForm, as it won't work. Please, check this post as might be of help to decide which one you wish to choose:
https://forums.asp.net/t/1757936.aspx?When+to+use+Html+BeginForm+vs+ajax+BeginForm
If you still want to use Html.BeginForm, just move the using sentence to replace your Ajax.BeginForm at the top of the page, so the form covers all fields, and the model won't be empty.

How to do validation from a viewmodel

I have a model with some validations below
public class RequestABook
{
[Required(ErrorMessage = "Name Required")]
[DisplayName("Book Name")]
public string BookName { get; set; }
[Required(ErrorMessage = "Zipcode Required")]
[DisplayName("Zipcode")]
public string ZipCode { get; set; }
[Required(ErrorMessage = "Contact Name Required")]
[DisplayName("Contact Name")]
public string ContactName { get; set; }
[Required(ErrorMessage = "Email Id Required")]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[Required(ErrorMessage = "Book Description Required")]
public string BookDescription { get; set; }
[Required(ErrorMessage = "You need to check one answer")]
public string Answer { get; set; }
}
I have a view model here
public class RequestViewModel
{
public RequestABook MyTestViewModel { get; set; }
}
I have my main page here that loads
#model BookRequestValidation.Models.RequestViewModel
#{
ViewBag.Title = "RequestABook";
}
<h2>RequestABook</h2>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Request Book</legend>
<div class="editor-label">
#Html.LabelFor(m => m.MyTestViewModel.BookName)
</div>
<div class="editor-field">
#Html.EditorFor(m => m.MyTestViewModel.BookName)
#Html.ValidationMessageFor(m => m.MyTestViewModel.BookName)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.MyTestViewModel.ZipCode)
</div>
<div class="editor-field">
#Html.EditorFor(m => m.MyTestViewModel.ZipCode)
#Html.ValidationMessageFor(m => m.MyTestViewModel.ZipCode)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.MyTestViewModel.ContactName)
</div>
<div class="editor-field">
#Html.EditorFor(m => m.MyTestViewModel.ContactName)
#Html.ValidationMessageFor(m => m.MyTestViewModel.ContactName)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.MyTestViewModel.Email)
</div>
<div class="editor-field">
#Html.EditorFor(m => m.MyTestViewModel.Email)
#Html.ValidationMessageFor(m => m.MyTestViewModel.Email)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.MyTestViewModel.BookDescription)
</div>
<div class="editor-field">
#Html.EditorFor(m => m.MyTestViewModel.BookDescription)
#Html.ValidationMessageFor(m => m.MyTestViewModel.BookDescription)
</div>
<div id="HCBudget" class="validation">
<label for="budgethealth">Budget Health</label>
#Html.RadioButton("Answer", "Red")
#Html.RadioButton("Answer", "Yellow")
#Html.RadioButton("Answer", "Green")
#Html.ValidationMessageFor(m => m.MyTestViewModel.Answer)
</div>
<input type="submit" value="Request Book" />
</fieldset>
}
Question: How do you guys handle validation with models used in a viewmodel.
Before I used this in a viewmodel everything was working well. By the time I used a viewmodel
validation stopped working.
Here is what the post action looks like.
public ActionResult RequestABook()
{
return View();
}
[HttpPost]
public ActionResult RequestABook(RequestABook quote)
{
return View();
}
It would help greatly if you posted your POST action. However, generally, I can say that validation is only run on related class instances if they are non-null. So, unless a value is posted for at least one of the properties on MyTestViewModel, it will not be instantiated by the modelbinder (MyTestViewModel will be null), and validation on its properties will not be run.
You can fix this scenario by always instantiating the MyTestViewModel property, either via the constructor of your view model or, probably better, using a custom getter and setter:
Constructor
public class RequestViewModel
{
public RequestViewModel()
{
MyTestViewModel = new RequestABook();
}
...
}
Custom Getter and Setter
private RequestABook myTestViewModel;
public RequestABook MyTestViewModel
{
get
{
if (myTestViewModel == null)
{
myTestViewModel = new RequestABook();
}
return myTestViewModel;
}
set { myTestViewModel = value; }
}

Model Validation with View Model which is inherited from Business Object

In MVC4 app, I have some Business Objects which am inheriting as view model. I want to use DataAnnotations in view model rather than specifying it in Business Object. Is that possible.
Business Objects
public class Country
{
[Display(Name = "Country")]
public virtual int CountryId { get; set; }
public virtual string CountryName { get; set; }
public virtual string CountryCode { get; set; }
}
public class State : Country
{
[Display(Name = "State")]
public virtual int StateId { get; set; }
public virtual string StateName { get; set; }
public virtual string StateCode { get; set; }
}
public class City : State
{
public virtual int CityId { get; set; }
[Display(Name = "City")]
public virtual string CityName { get; set; }
}
View Model
public class Regional
{
public Regional()
{
Countries = new Collection<Country>();
States = new Collection<State>();
City = new City();
}
public Collection<Country> Countries { get; set; }
public Collection<State> States { get; set; }
[Required(ErrorMessage = "Required")]
public City City { get; set; }
}
Here in the above code, I am using data annotations for City Property.
The problem here am facing is,even if I don't have this required annotation the form getting validated for Country and State but not validating for City Name.
When I add the required annotation, it is still getting validated only for country and state but not city name.
Any ideas?
My View
#using (Html.BeginForm(Actions.AddCity, Controllers.AdminUtility, FormMethod.Post))
{
<div class="alert-error">
#Html.ValidationSummary()
</div>
<div class="row-fluid">
<fieldset class="form-horizontal">
<legend class="header">Add City</legend>
<div class="control-group">
#Html.LabelFor(m => m.City.CountryId, new { #class = "control-label" })
<div class="controls">
#Html.DropDownListFor(m => m.City.CountryId, new SelectList(Model.Countries, "CountryId", "CountryName"),"", new { #class = "chosen", data_placeholder = "Choose Country..." })
</div>
</div>
<div class="control-group">
#Html.LabelFor(m => m.City.StateId, new { #class = "control-label" })
<div class="controls">
#Html.DropDownListFor(m => m.City.StateId,new SelectList(Model.States, "StateId","StateName"), "",new { #class = "chosen", data_placeholder = "Choose State..." })
</div>
</div>
<div class="control-group">
#Html.LabelFor(m => m.City.CityName, new { #class = "control-label" })
<div class="controls">
#Html.TextBoxFor(m => m.City.CityName)
</div>
</div>
<div class="control-group">
<div class="controls">
<input type="submit" />
</div>
</div>
</fieldset>
</div>
}
Your validation is saying that the class is required. You want the CityName to be required, so in your scenario, no, you have to specify the attributes on the domain model.
Just a note - I don't think your domain model is very clear. City, State, and Country don't inherit from one another, they are just related. A State is in a country, it isn't a country... Also, you should always validate your domain objects since they may be populated from multiple sources. Your ViewModel would then have additional validation that is specific to that view -- maybe it has some additional data used for ui processing that doesn't need to be passed on to your domain, etc.

MVC4 sending model propert id to controller class

How to send id of a model property Name to Controller class in an MVC4 application
public class{
public Name{get;set;}
}
For Accesing name using id of that property
Update:
Here if change Name using jquery at runtime i want to send the changed name id to the controller class
UPDate:
This is my VIew
<script type="text/javascript">
$(function () {
$('.editor input').blur(function () {
$(this).hide();
$(this).closest('p').find('label').html($(this).val()).show();
});
$('.editor label').click(function () {
$(this).hide();
$(this).closest('p').find('input').show();
});
});
#using (Html.BeginForm("Homepage", "Home", FormMethod.Post))
{
<div class="editor">
<p>
#Html.LabelFor(x => x.Name, Model.Name)
#Html.EditorFor(x => x.Name)
<input type="submit" value="OK" />
</p>
<p>
#Html.LabelFor(x => x.Company, Model.Company)
#Html.EditorFor(x => x.Company)
<input type="submit" value="OK" />
</p>
<p>
#Html.LabelFor(x => x.City, Model.City)
#Html.EditorFor(x => x.City)
<input type="submit" value="OK" />
</p>
</div>
<input type="submit" value="OK" />
}
This is my model
public class Details
{
public string Name
{ get; set; }
public string Company
{ get; set; }
public string City
{ get; set; }
}
This is my COntroller methods
public ActionResult Homepage(Details d)
{
d.Name = "Rakesh";
d.Company = "TCS";
d.City = "DElhi";
return View(d);
}
[HttpPost, ActionName("Homepage")]
public ActionResult Indexof(Details d)
{
return View(d);
}
Here i am editing and sending data to the controller but my problem is when i click on Rakesh for example and change the name then i need to click button twice then only the changed data is sent to the controller class
Model:
public class SomeModel {
public string Name { get; set; }
}
Controller:
[HttpPost]
public ActionResult YourAction( SomeModel m )
{
if( ModelState.IsValid )
{
// use model
var name = m.Name;
return RedirectToAction( "Index", "Home" );
}
return View( m );
}
If this isn't what you need, please clarify what's this "id" you're talking about.