Upload a HttpPostedFileBase for image but always null - asp.net-mvc

I am new to MVC and web programming.
My uploading image is working fine, but when I use almost the exact same code to allow user to modify its upload, it HttpPostedFileBase is always null. Driving me crazy...
here is the model
public class ModifyGenreViewModel
{
public int Id { get; set; }
[Display(Name = "Nom du style")]
public string Name { get; set; }
[Display(Name = "Image")]
public HttpPostedFileBase Image { get; set; }
}
and the view
#using (Html.BeginForm("ModifyGenre", "Upload", null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>#Resource.StyleCreationHeader</h4>
<hr />
#Html.ValidationMessageFor(model=>Model)
<div class="form-group" style="display:none">
#Html.LabelFor(model => model.Id, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-5">
#Html.EditorFor(model => model.Id, new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-5">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Image, new { #class = "control-label col-md-2" })
<div class="col-md-10">
<input type="file" data-val="true" id="ModifyGenreViewModel_Image" name="ModifyGenreViewModel.Image" />
#Html.ValidationMessageFor(model => model.Image, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="#Resource.Modify" class="btn btn-primary" />
</div>
</div>
</div>
}
When I set the breakpoint in my controller, I see the Id and the name, but Image is always null.
Thank's for your help!

The name property value of your file input should match with the view model property name for model binding to work properly.
Change the input field name to "Image"
<input type="file" data-val="true" id="ModifyGenreViewModel_Image" name="Image" />
Assuming your HttpPost action method accepts the ModifyGenreViewModel object as parameter.
[HttpPost]
public ActionResult ModifyGenre(ModifyGenreViewModel model)
{
// to do : return something
}

In model class change it to public String Image { get; set; }
In .cshtml page change it to #Html.TextBoxFor(m => m.Image, new {
type = "file", name="Image" })
In Controller you should use the name what you have decleared in html helper HttpPostedFileBase Image

Related

ASP.NET MVC - Compound Model

I have a trivial web app with the following model:
public class SillyModel
{
public SillyModel()
{ Id = Guid.NewGuid(); Children = new List<SillyModel>(); }
[Key]
public virtual Guid Id { get; set; }
public virtual string Value { get; set; }
public virtual List<SillyModel> Children { get; set; }
}
}
I have an Edit View of:
#model WebApplication1.Models.SillyModel
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.Partial("EditPartial", Model)
<div class="form-horizontal">
<h4>SillyModel</h4>
<hr />
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
With the Partial:
#model WebApplication1.Models.SillyModel
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.Id)
<div class="form-group">
#Html.LabelFor(model => model.Value, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Value, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Value, "", new { #class = "text-danger" })
</div>
#foreach (var item in Model.Children)
{
#Html.Partial("EditPartial", item)
}
</div>
The rendering is Fine! But for the life of me (well at least 3 days of struggle) I can not get it so that the returned model is properly bound! [No children are returned]
I am at my wits end.
You have to re-structure the way you are coding a little
In the Views/YourController, add a folder called EditorTemplates if not yet exists and add a view named SillyModel and copy the code from EditPartial to this new view
You change the foreach to for loop to decorate the controls with index
The code
~/Views/YourController/EditorTemplates/SillyModel.cshtml
#model WebApplication1.Models.SillyModel
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.Id)
<div class="form-group">
#Html.LabelFor(model => model.Value, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Value, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Value, "", new { #class = "text-danger" })
</div>
#for (var index=0; index<Model.Children.Count;index++)
{
#Html.EditorFor(model=>Model.Children[index])
}
</div>
~/Views/YourController/Edit
instead of #Html.Partial("EditPartial", Model), use #Html.EditorFor(m=>m)
Explanation
By adding the EditorTemplates/SillyModel , now you can call #Html.EditorFor(model=>Model.Children[index]) and your custom editor will be rendered
You need to use indexed controls in order Binding to the Model succeeded
Hope this will help you

One model for two submit buttons in ASP.NET MVC

I have a form on my view:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.DateFrom, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.DateFrom, new { htmlAttributes = new { #class = "form-control date-picker" } })
#Html.ValidationMessageFor(model => model.DateFrom, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.DateTo, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.DateTo, new { htmlAttributes = new { #class = "form-control date-picker" } })
#Html.ValidationMessageFor(model => model.DateTo, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" formAction=#Url.Action("CreateReport") />
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.EMail, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.EMail, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.EMail, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Send to email" class="btn btn-default" formAction=#Url.Action("SendEmail") />
</div>
</div>
</div>
}
As you can see I have two butons, first button call CreateReport action and than Send button call SendEmail action. I want to create report and then send this report by e-mail.
Here is my controller actions:
public ActionResult Index()
{
Report
report=ReportRepository.GetReport(DateTime.Parse("02.08.1996"), DateTime.Parse("07.08.1996"));
return View(report);
}
public ActionResult CreateReport(Report report)
{
report = ReportRepository.GetReport(report);
return View("Index", report);
}
public ActionResult SendEmail(Report report)
{
return View("Index", report);
}
And my model class:
public class Report
{
public DateTime DateFrom { get; set; }
public DateTime DateTo { get; set; }
public List<OrderDetails> Orders { get; set; }
[Display(Name = "Email address")]
[EmailAddress(ErrorMessage = "Invalid Email Address")]
public string EMail { get; set; }
}
So I mean that I fill Orders list in CreateReport action and display it and after it I press "Send to email" button, that's call "SendEmail" action, where I save Orders list to file and send it.
The problem is that in "SendEmail" action List is null.
How can I fix it?
The simplest way that I could think of is to remove your submit action for create report and handle this with ajax call. So that you will have only one submit action.
Or else you can try with 2 forms in your View.
Personally, I prefer the 1st option.
I'v found a solution. The solution is not to pass model to controller but store my List in Session. Like this:
public ActionResult Index()
{
Report report=ReportRepository.GetReport(DateTime.Parse("02.08.1996"), DateTime.Parse("07.08.1996"));
Session["Orders"] = report.Orders;
return View(report);
}
public ActionResult CreateReport(Report report)
{
report = ReportRepository.GetReport(report);
Session["Orders"] = report.Orders;
return View("Index", report);
}
public ActionResult SendEmail(Report report)
{
List<OrderDetails> orders = (List<OrderDetails>)Session["Orders"];
report.Orders = orders;
return View("Index", report);
}

How do I create a new db entry and populate the foreign key based on autocomplete selection of that foreign key object?

I have a ASP.NET MVC application i am developing, and have a repair model that includes a foreign key to a serial number model. In my view i have a typeahead jquery doing a autocomplete search through database to allow the user to select a serial number already in the database. I then have fields to fill out new information that will populate the repair table. I cannot seem to get the selected SerialNumber.Id to populate in the Repair.SerialNumber_Id from the selected serial number.
my view:
#model EntityTestApp.Models.Repair
#{
ViewBag.Title = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Repair</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.RepairDate, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.RepairDate, new {htmlAttributes = new { #class = "form-control", #Value = DateTime.Now.ToShortDateString() } })
#Html.ValidationMessageFor(model => model.RepairDate, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.SerialNumber, htmlAttributes: new { #class = "control-label col-md-2"})
<div class="col-md-10">
<input id="serialNumberTextBox" type="text" value="" class="form-control" />
#Html.ValidationMessageFor(model => model.SerialNumber, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.CustomerComplaint, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextAreaFor(model => model.CustomerComplaint, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.CustomerComplaint, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.IssueFound, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextAreaFor(model => model.IssueFound, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.IssueFound, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.RepairActionTaken, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextAreaFor(model => model.RepairActionTaken, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.RepairActionTaken, "", new { #class = "text-danger" })
</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{
<script>
$(document).ready(function () {
var SerialNumbers = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('Number'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: '/api/serialnumbers?query=%QUERY',
wildcard: '%QUERY'
}
});
$('#serialNumberTextBox').typeahead({
highlight: true
}, {
name: 'SerialNumbers',
display: 'Number',
source: SerialNumbers
}).on("typeahead:select", function (e, SerialNumber) {
//alert(serialNumberTextBox.value)
});
});
</script>
}
my Controller action to create a new repair entry:
// GET: Repairs/Create
public ActionResult Create()
{
return View();
}
// POST: Repairs/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Repair repair)
{
if (ModelState.IsValid)
{
db.Repairs.Add(repair);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(repair);
}
And my repair model:
public class Repair
{
public int Id { get; set; }
[DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}")]
public DateTime RepairDate { get; set; }
public SerialNumber SerialNumber { get; set; }
public string CustomerComplaint { get; set; }
public string IssueFound { get; set; }
public string RepairActionTaken { get; set; }
}
Ok, several hair pulling nights later i figured it out. I had to add a hidden field to hold the value for the foreign key and assign that value based on the 'typeahead' javascript autocomplete selection with "document.getElementById('').
Works like a charm. Thanks for the help!
-Chris

Submitting image path to the database

Im looking enter in some data and then save it into a DB table,The other fields work just fine when submitted to the database. but when it comes to saving the image path it gives null. Ive searched online and everything i find is out of date and doesn't work for the most part.
Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Insert([Bind(Include ="id, ImageName, ImageSize")] ImageInfo info, HttpPostedFileBase ImagePath)
{
if(ModelState.IsValid)
{
if (ImagePath != null)
{
var filename = Path.GetFileName(ImagePath.FileName);
var path = Path.Combine(Server.MapPath("~/Uploads"), filename);
ImagePath.SaveAs(path);
ImagePath.SaveAs(HttpContext.Server.MapPath("~/Uploads") + ImagePath.FileName);
info.ImagePath = ImagePath.FileName;
}
db.ImageInfoes.Add(info);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(info);
}
View
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Image</legend>
<div class="editor-label">
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.id, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.id, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.id, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ImageName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ImageName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ImageName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ImageSize, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ImageSize, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ImageSize, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#*#Html.TextBoxFor(model => model.ImagePath, new { type = "file" })*#
<input type="file" name="ImagePath" id="ImagePath" />
#Html.ValidationMessageFor(model => model.ImagePath, "", new { #class = "text-danger" })
</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>
</fieldset>
DB
Any help would be great!.
You want to add enctype="multipart/form-data" to form tag.
I personally like to rename HttpPostedFileBase parameter name to file not to confuse with ImageInfo.ImagePath.
View
#using (Html.BeginForm("Index","Start",FormMethod.Post,new {enctype="multipart/form-data"}))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Image</legend>
<div class="editor-label">
<div class="form-horizontal">
<hr/>
...
<div class="form-group">
#* Notice name and id are named as file *#
<input type="file" name="file" id="file"/>
#Html.ValidationMessageFor(model => model.ImagePath, "", new {#class = "text-danger"})
</div>
...
</div>
</div>
</fieldset>
}
Model
public class ImageInfo
{
public string Id { get; set; }
public string ImageName { get; set; }
public string ImageSize { get; set; }
public string ImagePath { get; set; }
}
Action Method
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index([Bind(Include = "id, ImageName, ImageSize")] ImageInfo info,
HttpPostedFileBase file)
{
if (ModelState.IsValid)
{
if (file != null)
{
file.SaveAs(Server.MapPath("~/Uploads") + file.FileName);
info.ImagePath = file.FileName;
}
/*db.ImageInfoes.Add(info);
db.SaveChanges();*/
return RedirectToAction("Index");
}
return View(info);
}

Why DefaultModelBinder doesn't bind route value ID from URL

ASP.NET MVC
In short:
I have a get action and a post action
when I type in browser localhost:port/Employee/Edit/1 I call get action, so in URL I have this whole url string. When I press submit button, in post action defaultmodelbinder doesnt bind id from URL!!!! I HAVE TO ADD HIDDEN FIELD for id. But why? I also have delete action (post), that gets id too, and I dont need to add hidden field for id. why?
More specifically:
I have the model:
public class EmployeeViewModel
{
public Int32 EmployeeId { get; set; }
public String Name { get; set; }
public String Phone { get; set; }
public String Email { get; set; }
public String Other { get; set; }
}
And 2 actions
public ActionResult Edit(int id)
{
try
{
EmployeeViewModel model;
using (var dbSession = NHibernateHelper.OpenSession())
{
var employee = dbSession.Query<Employee>().First(e => e.EmployeeId == id && e.ExpireDate==null);
model = new EmployeeViewModel(employee);
}
return View(model);
}
catch
{
return View("Error");
}
}
[HttpPost]
public ActionResult Edit(EmployeeViewModel model)
{
try
{
using (var dbSession=NHibernateHelper.OpenSession())
using (var transaction=dbSession.BeginTransaction())
{
var employee = model.ToEmployee();
dbSession.Merge(employee);
transaction.Commit();
}
return RedirectToAction("Index");
}
catch
{
return View("Error");
}
}
And 1 View (HERE I HAVE TO WRITE THIS LINE #Html.HiddenFor(model => model.EmployeeId) )
#using (Html.BeginForm()){
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.HiddenFor(model => model.EmployeeId)
#Html.LabelFor(model => model.Name, htmlAttributes: new {#class = "control-label col-md-2"})
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new {htmlAttributes = new {#class = "form-control"}})
#Html.ValidationMessageFor(model => model.Name, "", new {#class = "text-danger"})
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Phone, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Phone, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Phone, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Email, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Email, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Email, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Other, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Other, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Other, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Сохранить" class="btn btn-default" />
</div>
</div>
</div>}
Because the parameter in the method is named id and the property in
your model is named EmployeeId They are not the same. And if you
change the model property to Id it will be bound
Thanks, Stephen Muecke

Resources