mvc model fileupadload does not send model data - file-upload

I have a mvc 5 asp.net file upload that upload pictures and create path for them.
File uploaded successfully, but model data does comes in null.
This is my model:
[Table("Slider")]
public partial class Slider
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Slider()
{
Slider1 = new HashSet<Slider>();
}
public int ID { get; set; }
public string Path { get; set; }
public int? Slider_ID { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Slider> Slider1 { get; set; }
}
This is Controller part:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ID,Path")] Slider slider, List<HttpPostedFileBase> FileContent)
{
if (ModelState.IsValid)
{
byte[] imageData = null;
if (Request.Files.Count > 0)
{
HttpPostedFileBase poImgFile = Request.Files["Path"];
using (var binary = new BinaryReader(poImgFile.InputStream))
{
imageData = binary.ReadBytes(poImgFile.ContentLength);
}
}
string picturePath = string.Format(Server.MapPath("~/content/slider/{0}.jpg"), slider.ID);
CreateDirectory(picturePath);
using (FileStream writer = new FileStream(picturePath, FileMode.Create))
{
writer.Write(imageData, 0, imageData.Length);
}
db.Sliders.Add(slider);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(slider);
}
And this is the view:
#using (Html.BeginForm("Create", "Sliders", FormMethod.Post, new { #class = "form-horizontal", role = "form", enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="col-12 form-group">
<div class="row">
#Html.LabelFor(model => model.Path, "Picture", htmlAttributes: new { #class = "control-label col-12 col-md-2" })
<div class="col-12 col-md-10">
<input type="file" name="Path" id="fileUpload" accept=".png,.jpg,.jpeg,.gif" />
#Html.ValidationMessageFor(model => model.Path, "", new { #class = "text-danger" })
</div>
</div>
</div>
<div class="form-group">
<div class="col-12 text-left">
<input type="submit" value="create" class="btn btn-success" /> | #Html.ActionLink("back to list", "Index", null, new { #class = "btn btn-primary" })
</div>
</div>
</div>
}
When I check my database I see that Path is :
System.Web.HttpPostedFileWrapper
and Slider_ID is null and Slider_ID1 is null too.
Any suggestions?

After searching my previous codes, I've found that, when saving changes to db in controller class, db.SaveChanges(); slider argument which had been sent to Create method, will get new ID.
I've added these 2 lines after if:
if (ModelState.IsValid)
{
db.Sliders.Add(slider);
db.SaveChanges();
and done my other business logic in lines after that.

Related

MVC save image to DB (with EF)

I am still new to MVC and am struggling with uploading an image to a DB in my web application. I've seen tons of articles on the subject, followed instructions but still I am stuck with the following error message:
The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters.
Whenever I use [Bind(Exclude = "CompetitionPicture")], everything would work fine, except for, of course, the "CompetitionPicture" won't be included.
My ViewModel looks as follows:
public class PhotoCompetition
{
public int ID { get; set; }
public string UserID { get; set; }
public string FirstName { get; set; }
public string Email { get; set; }
public byte[] CompetitionPicture { get; set; }
[Required]
[Display(Name = "by checking this box I accept the Terms & Conditions")]
public bool TermsAndConditionsAccepted { get; set; }
public DateTime TimeStamp { get; set; }
}
Controller is as follows:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult UploadCompetitionPicture(/*[Bind(Exclude = "CompetitionPicture")]*/ PhotoCompetition model)
{
string test = Request.Form["CompetitionPicture"];
byte[] bt = Convert.FromBase64String(test.Split(',')[1]);
var participation = new PhotoCompetition
{
CompetitionPicture = bt
};
// var participation = new PhotoCompetition
// {
// UserID = User.Identity.GetUserId(),
// Email = User.Identity.GetUserName(),
// TermsAndConditionsAccepted = model.TermsAndConditionsAccepted,
// TimeStamp = DateTime.UtcNow.ToUniversalTime()
// };
// participation.CompetitionPicture = competitionPicture;
// DB.PhotoCompetition.Add(model);
// DB.SaveChanges();
// return RedirectToAction("Index");
//}
return View(model);
}
and the View:
<section id="photoCompetition" class="manageForm">
#using (Html.BeginForm("UploadCompetitionPicture", "errandom", FormMethod.Post, new { #id = "photoCompetitionForm", #class = "form-horizontal", #role = "form", #enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div id="photoCompetitionSection" class="manageSection">
<p id="photoCompetitionSectionTitle" class="manageSectionTitle">
Upload your picture and be selected as our model!
</p>
#Html.HiddenFor(m => m.UserID)
#Html.HiddenFor(m => m.Email)
#Html.HiddenFor(m => m.FirstName)
#Html.HiddenFor(m => m.TimeStamp)
<div id="photoCompetitionProfilePictureArea" class="manageArea row">
#Html.LabelFor(m => m.CompetitionPicture, new { #id = "photoCompetitionProfilePictureLabel", #class = "manageLabel col-xs-offset-1 col-xs-10 col-sm-offset-1 col-sm-10 col-md-offset-1 col-md-3 col-lg-offset-1 col-lg-4" })
<a id="photoCompetitionProfilePictureSelectionButton" class="manageField col-xs-offset-1 col-xs-10 col-sm-offset-1 col-sm-10 col-md-offset0 col-md-7 col-lg-offset-0 col-lg-6" href="#">
select a file...
</a>
#Html.TextBoxFor(m => m.CompetitionPicture, new { #id = "photoCompetitionProfilePictureField", #class = "manageField col-xs-offset-1 col-xs-10 col-sm-offset-1 col-sm-10 col-md-offset-0 col-md-7 col-lg-offset-0 col-lg-6", #name = "CompetitionPicture", #type = "file", #style = "display: none" })
</div>
<div id="photoCompetitionTermsAndConditionsArea" class="manageArea row">
#Html.CheckBoxFor(m => m.TermsAndConditionsAccepted, new { #id = "photoCompetitionTermsAndConditionsField", #class = "photoCompetitionTermsAndConditionsField" })
#Html.LabelFor(m => m.TermsAndConditionsAccepted, new { #id = "photoCompetitionTermsAndConditionsLabel", #class = "photoCompetitionTermsAndConditionsLabel" })
</div>
<script>
jQuery("#photoCompetitionProfilePictureSelectionButton").click(function () {
$("#photoCompetitionProfilePictureField").click();
});
</script>
<script>
$("#photoCompetitionProfilePictureField").change(function () {
var fullFileName = $("#photoCompetitionProfilePictureField").val()
$("#photoCompetitionProfilePictureSelectionButton").html(fullFileName.substr(fullFileName.lastIndexOf('\\') + 1));
});
</script>
<div id="photoCompetitionButtonArea" class="manageArea row">
<input id="photoCompetitionButtonUpload" class="manageButton col-xs-offset-1 col-xs-10 col-sm-offset-1 col-sm-10 col-md-offset-1 col-md-10 col-lg-offset-1 col-lg-10" type="submit" value="Save" />
</div>
</div>
}
</section>
Your support is highly appreciated!
I'm stuck in this issue for days before, i will put simple solving.
first: I save image path as string in DB not byte
and Image File as HttpPostedFileBase in model Like :`
public string ProductImage { get; set; }
[Required(ErrorMessage = "Image is required")]
public HttpPostedFileBase ImageFile { get; set; }
in view you will write :
<div class="form-group">
<div class="col-md-10">
<input type="file" name="ImageFile" required />
</div>
#Html.ValidationMessageFor(model => model.ImageFile, "", new { #class = "text-danger" })
</div>
name of input must be like the name in model and here I named it ImageFile
in controller will write
public void SaveImage(Product pro)
{
string fileName = Path.GetFileNameWithoutExtension(pro.ImageFile.FileName);
string exetention = Path.GetExtension(pro.ImageFile.FileName);
fileName = fileName + DateTime.Now.ToString("yymmssfff") + exetention;
pro.ProductImage = "~/ProductDropBox/" + fileName;
fileName = Path.Combine(Server.MapPath("~/ProductDropBox/"), fileName);
pro.ImageFile.SaveAs(fileName);
}
I use this function to save image in DB and in Folder created for images
finally in action result you will write :
public ActionResult AddProducts(Product pro)
{
if (ModelState.IsValid)
{
SetCategories();
SaveImage(pro);
pro.UPID = Guid.NewGuid();
using (db)
{
db.Products.Add(pro);
db.SaveChanges();
}
return RedirectToAction("Index");
}
else
{
return View("AddProducts",pro);
}
}
I hope this useful

ASP.NET MVC - Object reference not set to an instance of an object in DropDownList

I have a model Class
public partial class FEES
{
public FEES()
{
}
public long FEE_ID { get; set; }
public decimal AMOUNT { get; set; }
public int CURRENCY_ID { get; set; }
public string NAME { get; set; }
public virtual CURRENCIES CURRENCIES { get; set; }
}
ViewModel
public class FeesViewModel
{
public SelectList CurrenciesList { get; set; }
public FeesViewModelInput input { get; set; }
public class FeesViewModelInput
{
[HiddenInput]
public long FEE_ID { get; set; }
[Display(Name = "Amount")]
[Required(ErrorMessage = "Fee Amount Is Required!")]
[RegularExpression(#"^[0-9,.]+$", ErrorMessage = "Please enter proper currency format e.g. 2,500")]
public decimal AMOUNT { get; set; }
[Display(Name = "Currency")]
[Required(ErrorMessage = "Currency Is Required!")]
public int CURRENCY_ID { get; set; }
[Required(ErrorMessage = "Fee Name Is Required!")]
[Display(Name = "Fee Name")]
public string NAME { get; set; }
}
}
Small service for the ViewModel
public void createFees(FEES fee, FeesViewModel viewModel)
{
fee.FEE_ID = viewModel.input.FEE_ID;
fee.CURRENCY_ID = viewModel.input.CURRENCY_ID;
fee.NAME = viewModel.input.NAME.Trim();
}
I call the service and the ViewModel in my controller.
Controller
public ActionResult Create()
{
FeesViewModel fees = new FeesViewModel();
fees.CurrenciesList = new SelectList(_currenciesService.GetCurrencies().Where(c => c.ACTION_STATUS != 2), "CURRENCY_ID", "CURRENCY_NAME");
fees.FeeTypesList = new SelectList(_feetypesService.GetFeeTypes().Where(c => c.ACTION_STATUS != 2), "FEE_TYPE_ID", "FEE_TYPE_NAME");
return View();
}
[HttpPost]
public ActionResult Create(FeesViewModel fees)
{
try
{
if (ModelState.IsValid)
{
//check if values is duplicate
if (_feesService.GetFees().Where(c => c.ACTION_STATUS != 2).Any(c => c.NAME.ToLower().Trim() == fees.input.NAME.ToLower().Trim()))
{
this.AddNotification("Fee Name already exist.<br/> Kindly verify the data.", NotificationType.ERROR);
}
else
{
var fee = new BPP.CCSP.Admin.Web.BPPCCSPAdminFeesService.FEES();
var helper = new FeesService();
helper.createFees(fee, fees);
_feesService.AddFee(fee);
var notif = new UINotificationViewModel()
{
notif_message = "Record saved successfully",
notif_type = NotificationType.SUCCESS,
};
TempData["notif"] = notif;
return RedirectToAction("Index");
}
}
}
catch (Exception e)
{
this.AddNotification("Fees cannot be added.<br/> Kindly verify the data.", NotificationType.ERROR);
}
fees.CurrenciesList = new SelectList(_currenciesService.GetCurrencies().Where(c => c.ACTION_STATUS != 2), "CURRENCY_ID", "CURRENCY_NAME");
return View(fees);
}
And the View
#model BPP.CCSP.Admin.Web.ViewModels.FeesViewModel
#{
//ViewBag.Title = "Create";
}
<div class=" box box-body box-primary">
#using (Html.BeginForm("Create", "Fees", FormMethod.Post, new { #class = "form-horizontal", #enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true, null, new { #class = "text-danger" })
#*#Html.HiddenFor(model => model.faculty_activation_date, new { #Value = System.DateTime.Now })*#
<div class="row .col">
<div style="margin-top:20px" class="mainbox col-md-12 col-md-offset-0 col-sm-8 col-sm-offset-2">
<div class="panel panel-info">
<div class="panel-heading">
<div class="panel-title">Create Fee</div>
</div>
<div class="panel-body">
<div class="col-md-6">
<div>
#Html.LabelFor(model => model.input.NAME, "Fee Name")
#Html.TextBoxFor(model => model.input.NAME, new { #style = "border-radius:3px;", #type = "text", #class = "form-control", #placeholder = Html.DisplayNameFor(m => m.input.NAME), #autocomplete = "on" })
#Html.ValidationMessageFor(model => model.input.NAME, null, new { #class = "text-danger" })
</div>
<div>
#Html.LabelFor(model => model.input.AMOUNT, "Amount")
#Html.TextBoxFor(model => model.input.AMOUNT, new { #style = "border-radius:3px;", #type = "text", #class = "form-control", #placeholder = Html.DisplayNameFor(m => m.input.AMOUNT), #autocomplete = "on" })
#Html.ValidationMessageFor(model => model.input.AMOUNT, null, new { #class = "text-danger" })
</div>
</div>
<div class="col-md-6">
<div>
#Html.LabelFor(model => model.input.CURRENCY_ID, "Currency")
#*#Html.DropDownList("CURRENCY_ID", (IEnumerable<SelectListItem>)ViewBag.name, "Please Select a Currency", new { #class = "form-control", #style = "border-radius:3px;" })*#
#Html.DropDownListFor(x => x.input.CURRENCY_ID, Model.CurrenciesList, "Please Select a Currency", new { #class = "form-control", #style = "border-radius:3px;" })
#Html.ValidationMessageFor(model => model.input.CURRENCY_ID, null, new { #class = "text-danger" })
</div>
<div>
#Html.LabelFor(model => model.input.FEE_TYPE_ID, "Fee Type")
#Html.DropDownListFor(model => model.input.FEE_TYPE_ID, Model.FeeTypesList, "Please Select a Fee Type", new { #class = "form-control", #style = "border-radius:3px;" })
#Html.ValidationMessageFor(model => model.input.FEE_TYPE_ID, null, new { #class = "text-danger" })
</div>
</div>
</div>
<div class="panel-footer">
<div class="panel-title">
<div class="form-actions no-color">
<input type="submit" value="Create" class="btn btn-success" />
</div>
</div>
</div>
</div>
</div>
</div>
</div>
}
</div>
}
When I clicked on the View (Create), I got this error
The CurrencyID is a DropDownList coming from CURRENCIES model class.
I have these questions:
Why am I getting this error and how do I resolve it.
How do I do ViewModel without mapping.?
Why am I getting this error and how do I resolve it.
Because the Model is not set in your view. It is null.
When the users visit the Create page, you need to make sure to present them with options in the dropdown. Therefore, you need to make sure you pass the model into the view during GET.
public ActionResult Create()
{
// your code and pass fees to your view.
return View(fees);
}
How do I do ViewModel without mapping. Any example please.
You can use AutoMapper NuGet package to do the mapping.

MVC - Foreign Key Error because of ViewModel?

I am Creating View Model of Employee Class and strongly typed my Create View with the EmployeeViewModel. In future, I will add many classes in my View Model. But the problem is I am getting Gender Foreign Key Error. May be I am binding wrong values in Create Controller. Below is my code:
Create Controllers:
public ActionResult Create()
{
ViewBag.GenderId = new SelectList(db.Genders, "Id", "Name");
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(EmployeeViewModel employeeModel)
{
if (ModelState.IsValid)
{
db.Employees.Add(employeeModel.Employee);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.GenderId = new SelectList(db.Genders, "Id", "Name", employeeModel.Employee.GenderId);
return View(employeeModel);
}
Create View:
#model WebApplication2.EmployeeViewModel
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Employee</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Employee.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Employee.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Employee.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Employee.GenderId, "GenderId", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("GenderId", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.Employee.GenderId, "", 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>
}
Models:
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public byte GenderId { get; set; }
public Gender Gender { get; set; }
}
public class Gender {
public byte Id { get; set; }
public string Name { get; set; }
}
View Model:
public class EmployeeViewModel
{
public Employee Employee { get; set; }
}

ASP.Net MVC: When form post then one view model property getting null

i have simple form where one dropdown and one submit button. i have two index function one for get and one for form post. when i select a product from dropdown and click on submit button then my index action getting invoke but there i notice my products property getting null. see my code please and tell me where i made the mistake.
view code
#model AuthTest.Models.SampleViewModel
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>DateValTest</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Products, htmlAttributes: new { #class = "control-label col-md-2", style = "padding-top:0px;" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.SelectedProductId, new SelectList(Model.Products, "ID", "Name"), "-- Select Product--")
#Html.ValidationMessageFor(model => model.SelectedProductId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Submit" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
model code
public class Product
{
public int ID { set; get; }
public string Name { set; get; }
}
public class SampleViewModel
{
[Display(Name = "Products")]
public List<Product> Products { set; get; }
[Required(ErrorMessage = "Select any one")]
public int SelectedProductId { set; get; }
}
controller code
public class TestValController : Controller
{
// GET: TestVal
public ActionResult Index()
{
var SampleVM = new SampleViewModel();
SampleVM.Products = new List<Product>
{
new Product{ ID=1, Name="IPhone" },
new Product{ ID=2, Name="MacBook Pro" },
new Product{ ID=3, Name="iPod" }
};
return View(SampleVM);
}
[HttpPost]
public ActionResult Index(SampleViewModel vm)
{
var SampleVM = new SampleViewModel();
SampleVM.Products = new List<Product>
{
new Product{ ID=1, Name="IPhone" },
new Product{ ID=2, Name="MacBook Pro" },
new Product{ ID=3, Name="iPod" }
};
if (ModelState.IsValid)
return View(vm);
else
return View(SampleVM);
}
}
when i debug second action then i saw vm products property getting null
please tell me where i made the mistake?
thanks
You are not making any mistake, You are not getting the list of products back because you are not including them in the HTML input form.
If you want to include the list of products you can add the following inside the input form
#for (int i = 0; i < Model.Products.Count(); i++)
{
<div>
#Html.HiddenFor(model => Model.Products[i].Name)
#Html.HiddenFor(model => Model.Products[i].ID)
</div>
}
#Mou,
Please modify your razor view and try this.
In Razor View nowhere you have specified the Http verb(Get,Post).
#using (Html.BeginForm("Index", "TestVal", FormMethod.Post)

ajax beginform mvc callback

I have a single page with multiple partials set up. I want to be able to validate and update each partial seperatly. The validation works BUT when I type in a correct value and press save the page goes to the partial view instead of staying on the single page. What am I doing wrong here?
This is my main page :
#for (var i = 0; i < 10; i++)
{
var idTest = "Test_" + i;
<div id="#idTest">
#Html.Action("Detail", new { id = i })
</div>
}
The partial is created like this:
#{
var idTest = "Test_" + Model.Id;
var ajaxOptions = new AjaxOptions
{
UpdateTargetId = #idTest,
HttpMethod = "POST",
InsertionMode = InsertionMode.Replace
};}
#using (Ajax.BeginForm("Detail", ajaxOptions))
{ #Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Test</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Gemeente, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBoxFor(model => model.Gemeente, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Gemeente, "", new { #class = "text-danger" })
</div>
</div>
<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>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
This is the simple model:
public class Test
{
public int Id { get; set; }
[Required(ErrorMessage = "Gelieve een gemeente op te geven")]
public string Gemeente { get; set; }
}
These are the actions:
[HttpGet]
public ActionResult Detail(int id)
{
Models.Test model = new Models.Test();
model.Id = id;
return View(model);
}
[HttpPost]
public ActionResult Detail(Models.Test model)
{
if(ModelState.IsValid)
{
return PartialView(model);
}
return PartialView(model);
}
Add these lines to your view and also use #Html.Partial as shown below
<script src="~/Scripts/jquery-1.8.2.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
so that your main view is
<script src="~/Scripts/jquery-1.8.2.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
#for (var i = 0; i < 10; i++)
{
var idTest = "Test_" + i;
<div id="#idTest">
#Html.Partial("Detail", new Test { Id = i })}
</div>
}
Scripts would be for unobtrusive ajax so that your ajax button works and Html.Partial so that first time when you load your page in foreach only partial view is rendered (not the full view)