I have the edit view and the edit action in my blog controller.
After I created a post with the "create action" and after I upload the image to the database folder, I update the path on the post.PostImage (string value).
I can see the image in the folder, and I can see the path of my image and I can also see the preview of the picture in the edit view. In my database it saved as (~/Images/PostID/PictureName). But after I edit my post, I want to make a checkbox that if checked I can edit the picture and when is it not checked I delete the picture. I send the parameters, and my problem is that on the debugger I see the "string postimage" as null but on the database table it has the path!
and because of that all of this doesn't work, I don't care about logic, why is it null????
here is my code:
VIEW:
#model WebApplication1.Models.Post
#{
ViewBag.Title = "Edit";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Edit</h2>
#using (Html.BeginForm(Html.BeginForm("Edit", "Blog", FormMethod.Post, new { enctype = "multipart/form-data" })))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Post</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.PostID)
<div class="form-group">
#Html.LabelFor(model => model.PostTitle, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.PostTitle, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.PostTitle, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.PostAuthor, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.PostAuthor, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.PostAuthor, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.WebSite, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.WebSite, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.WebSite, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.PostDate, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.PostDate, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.PostDate, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.PostText, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.PostText, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.PostText, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div>
<b>Upload image:</b>
#if (!Model.PostImage.IsEmpty())
{
#Html.CheckBox("checkImage", true)
<img src="#Url.Content(Model.PostImage)" alt="#Model.PostAuthor" width="300" />
}
else
{
#Html.CheckBox("checkImage", false)
}
</div>
<input type="file" name="file" id="file" />
<!-- Show message of the controller -->
#ViewBag.Message
</div>
<div class="form-group">
#Html.LabelFor(model => model.PostVideo, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.PostVideo, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.PostVideo, "", 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>
}
<div>
#Html.ActionLink("Back to Posts List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
EDIT ACTION IN BLOG CONTROLLER:
// GET: Blog/Edit/5
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Post post = db.Posts.Find(id);
if (post == null)
{
return HttpNotFound();
}
return View(post);
}
// POST: Blog/Edit/5
// 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 Edit([Bind(Include = "PostID,PostTitle,PostAuthor,WebSite,PostDate,PostText,PostImage,PostVideo")] Post post, HttpPostedFileBase file, bool checkImage)
{
var fileName = "";
if (ModelState.IsValid)
{
db.Entry(post).State = EntityState.Modified;
if (checkImage == true)
{
//Check if there is a file
if (file != null && file.ContentLength > 0)
{
//Check if there is an image
var supportedTypes = new[] { "jpg", "jpeg", "gif", "png" };
var fileExt = System.IO.Path.GetExtension(file.FileName).Substring(1);
if (!supportedTypes.Contains(fileExt))
{
ViewBag.Message = "Invalid image type. Only the following types (jpg, jpeg, gif, png) are supported";
return View();
}
//Check if there is a file on database
if ( !(String.IsNullOrEmpty(post.PostImage)) )
{
//Delete old file in folder
System.IO.File.Delete(post.PostImage);
//Save new file in folder
var folder = Path.Combine(Server.MapPath("~/Images/"), Convert.ToString(post.PostID));
var path = Path.Combine(folder, fileName);
file.SaveAs(path);
//Save path in database
string targetPath = String.Concat("~/Images/", Convert.ToString(post.PostID), "/", fileName);
post.PostImage = targetPath;
}
//No file in database
else
{
var folder = Path.Combine(Server.MapPath("~/Images/"), Convert.ToString(post.PostID));
var path = Path.Combine(folder, fileName);
file.SaveAs(path);
//Save path in database
string targetPath = String.Concat("~/Images/", Convert.ToString(post.PostID), "/", fileName);
post.PostImage = targetPath;
}
}
//Checkbox is checked but not file uploaded
else
ViewBag.Message = "Checkbox is checked, please upload an image";
return View();
}
else
{
//Checkbox is not checked - Delete the image from database
if( !(String.IsNullOrEmpty(post.PostImage)) )
{
//Delete old file in folder
try
{
System.IO.File.Delete("\a.txt");
post.PostImage = null;
}
catch (System.IO.IOException e)
{
Console.WriteLine(e.Message);
}
}
db.SaveChanges();
return RedirectToAction("Index");
}
}
return View(post);
}
Render the postImage field
#Html.EditorFor(model => model.PostImage, new { htmlAttributes = new { #class = "form-control" } })
So from what I have read recently, do not pass your Entity Framework model directly into your view. Create a separate ViewModel. On GET, Construct this ViewModel based on your EF model, and on POST, extract the information you need from your ViewModel and update your EF model that is in the database.
In your View, you don't have a EditorFor() or HiddenFor() for the image URL, and that is why on your POST it will be null.
This is EXACTLY why you want to use ViewModels instead of directly using EF models in your view so you can have a separate view model that only contains property that needs to be edit/updated/displayed, while properties from the entities that needs to be remained untouched will remain untouched.
Related
I want to get the description and price of the selected artikel, but i want to be able to edit it afterwards so for example you have a standard price which can be adjusted.
Here is what my database looks like.
Here is my view code. (I left out some input fields)
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-group">
#Html.LabelFor(model => model.ArtikelID, "ArtikelID", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("ArtikelID", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.ArtikelID, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ArtikelBeschrijving, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ArtikelBeschrijving, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ArtikelBeschrijving, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ArtikelPrijs, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ArtikelPrijs, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ArtikelPrijs, "", 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>
and the controller for the view.
// GET: Orders/Create
public ActionResult Create()
{
ViewBag.ArtikelID = new SelectList(db.Artikels, "ArtikelID", "ArtikelNummer");
ViewBag.PaardID = new SelectList(db.Paarden, "PaardID", "Naam");
return View();
}
// POST: Orders/Create
// To protect from overposting attacks, enable the specific properties you want to bind to, for
// more details see https://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "OrderID,Ordernummer,PaardID,KlantNaam,PaardNaam,ChipNummer,Levensnummer,ArtikelID,ArtikelBeschrijving,ArtikelPrijs,Datum")] Orders orders)
{
if (ModelState.IsValid)
{
db.Orders.Add(orders);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.ArtikelID = new SelectList(db.Artikels, "ArtikelID", "ArtikelNummer", orders.ArtikelID);
ViewBag.PaardID = new SelectList(db.Paarden, "PaardID", "Naam", orders.PaardID);
return View(orders);
}
This is essentially what i want.
I've tried searching everywhere, but i couldn't find anything similar to what i want. Hopefully you guys can help me out.
Below is a work demo, you can refer to it.
1.Add id to ArtikelID, ArtikelBeschrijving,ArtikelPrijs, like below:
#Html.DropDownList("ArtikelID", null, htmlAttributes: new { #class = "form-control" ,#id="list" })
#Html.EditorFor(model => model.ArtikelBeschrijving, new { htmlAttributes = new { #class = "form-control" ,#id="id01"} })
#Html.EditorFor(model => model.ArtikelPrijs, new { htmlAttributes = new { #class = "form-control", #id="id02" } })
2.Add below <script> to your Create view.
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script type="text/jscript">
$(function() {
$("#list").change(function() {
var selectedid = $('option:selected').val();
$.ajax({
url: "/YourControllerName/GetDropDownList",
data: {
id: selectedid
},
type: "Post",
dataType: "Json",
success: function(result) {
document.getElementById('id01').value = result.beschrijving;
document.getElementById('id02').value =result.prijs;
},
error: function() {
}
});
});
});
</script>
3.In your Controller, add GetDropDownList action like below:
[HttpPost]
public IActionResult GetDropDownList(int id)
{
var result = db.Artikels.FirstOrDefault(m => m.ArtikelID == id);
return Json(result);
}
Result:
Update get id
I'm working on Visual Studio 2017 using Asp.net Mvc 5 to build a web site,My problem is that I can't upload an image from a input file to a server folder
MY CODE:
Create: Views
#using (Html.BeginForm("Create","Jobs",null,FormMethod.Post,new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Jobs</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.JobTitle, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.JobTitle, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.JobTitle, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.JobContent, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.JobContent, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.JobContent, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.JobImage, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<input type="file" name="upload"/>
#Html.ValidationMessageFor(model => model.JobImage, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.CategoryId, "JobType", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("CategoryId", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.CategoryId, "", 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" />
#ViewBag.Message
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Controller:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create( Jobs jobs,HttpPostedFile upload)
{
try
{
if (ModelState.IsValid)
{
if (upload != null && upload.ContentLength > 0)
{
string FileName = Path.GetFileName(upload.FileName);
string path = Path.Combine(Server.MapPath("~/Uploads"), FileName);
upload.SaveAs(path);
jobs.JobImage = FileName;
db.Jobs.Add(jobs);
db.SaveChanges();
}
else{
}
}
}
catch (RetryLimitExceededException /* dex */)
{
//Log the error (uncomment dex variable name and add a line here to write a log.
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
}
ViewBag.CategoryId = new SelectList(db.Categories, "Id", "CategoryName", jobs.CategoryId);
return View(jobs);
}
when I click submit Button nothing change on browser and no error is generated, but when I check Uploads file I found nothing(img file ) ,the same on the database , I don't know where is the error
Note:
Before I added the input file , my code was working perfectly
Any Help Please
Ok So I have been searching all over have come to this little bit of code and got almost everything to work. I can upload an image and save it to the folder that was created. I can save the the other values pass to the database as well. My problem now is that i want to save the file path to the data base so i can call it to display the image on another page. For some reason it will only save the image's name that is being uploaded and not the path.
When i debug it and see what all is getting passed, when i add a watch to
file.SaveAs(pathLoc);
Expression has been evaluated and has no value void
Here is the code I've beed using
Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ImageInfo info, HttpPostedFileBase file)
{
if (ModelState.IsValid)
{
if (file != null)
{
var pic = Path.GetFileName(file.FileName);
var pathLoc = Path.Combine(Server.MapPath("~/Uploads/") + pic);
file.SaveAs(pathLoc);
info.ImagePath = file.FileName;
db.SaveChanges();
}
return RedirectToAction("Index");
}
return View(info);
}
View
<h2>Create</h2>
#using (Html.BeginForm("Create", "ImageInfoes", null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<div class="form-horizontal">
<h4>ImageInfo</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<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.LabelFor(model => model.ImagePath, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<input id="ImagePath" title="Upload a product image" type="file" name="file" />
</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>
Model
public partial class ImageInfo
{
public byte id { get; set; }
public string ImageName { get; set; }
public Nullable<long> ImageSize { get; set; }
public string ImagePath { get; set; }
}
Anything would be help full.
If you want, full path you need to combine file path and file name both.
Console.WriteLine(Path.Combine(System.Reflection.Assembly.GetExecutingAssembly().Location, System.IO.Path.GetRandomFileName()));
C:\Users\02483695\Documents\Visual Studio
2015\Projects\ConsoleApplication5\Cons
oleApplication5\bin\Debug\ConsoleApplication5.exe\tv1k1uev.jsq
Also below you may find a sample about file, file directory, file path and file extension.
System.IO.FileInfo fileInfo = new FileInfo("filename.file");
var fileDirectory = fileInfo.DirectoryName;
var fileName = fileInfo.Name;
var fileExtension = fileInfo.Extension;
var filePathandFileNameBoth = fileInfo.FullName;
Console.WriteLine("filePathandFileNameBoth: ");
Console.WriteLine(filePathandFileNameBoth);
Console.WriteLine("-");
Console.WriteLine("fileDirectory:");
Console.WriteLine(fileDirectory);
Console.WriteLine("-");
Console.WriteLine("fileName:");
Console.WriteLine(fileName);
Console.WriteLine("-");
Console.WriteLine(filePathandFileNameBoth == fileDirectory + "\\" + fileName ? fileExtension : "I'm totally wrong");
Console.ReadLine();
as a result:
I'm just trying record student information with an image profile. I want to upload the image as part of the form to application->Image directory and save the image name to the database.
This is my controller
public ActionResult Create([Bind(Include ="StudentId,StudentName,StudentLastName,StudentPhone,StudentAge,StudentEmail,photo")] Student student , HttpPostedFileBase file)
{
if (ModelState.IsValid)
{
file.SaveAs(HttpContext.Server.MapPath("~/Images/") + file.FileName);
db.Students.Add(student);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(student);
}
in my view
#using (Html.BeginForm("Create", "Students", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Student</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.StudentName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.StudentName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.StudentName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.StudentLastName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.StudentLastName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.StudentLastName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.StudentPhone, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.StudentPhone, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.StudentPhone, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.StudentAge, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.StudentAge, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.StudentAge, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.StudentEmail, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.StudentEmail, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.StudentEmail, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.photo, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBoxFor(model => model.photo, new { type = "file" })
#Html.ValidationMessageFor(model => model.photo, "", 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>
}
but when i upload, it generates an error message
An exception of type 'System.NullReferenceException'
on this line
file.SaveAs(HttpContext.Server.MapPath("~/Images/") + file.FileName
please help me this issue please...
Try this one:
change it:
From
file.SaveAs(HttpContext.Server.MapPath("~/Images/") + file.FileName
To
file.SaveAs(HttpContext.Server.MapPath("~/Images/" + file.FileName))
System.NullReferenceException means that something is null at this reported line of code.
In this situation, it should be your HttpPostedFileBase file.
Try run the application in debug mode, and check again the name of your upload field, whether it's set as "file". MVC asp.net using Name attribute to define parameter.
In my case, I'm using a simple <input> to do the upload file and another <img> for displaying:
View:
<!--Displaying: You need some code for this-->
<img src="#ViewBag.ImagePath" alt="Message picture" style="width:100%;">
<!--Uploading-->
#using (Html.BeginForm("Create", "Students", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<!-- Your other part of form -->
<!-- Uploading -->
<input type="file" name="file" />
}
Controller:
// extract only the filename
var fileName = Path.GetFileName(file.FileName);
// initial new path for upload file
var newSPath = Path.Combine(Server.MapPath("~/Images"), fileName);
// store file
file.SaveAs(newSPath);
Try this:-
public ActionResult Create([Bind(Include ="StudentId,StudentName,StudentLastName,StudentPhone,StudentAge,StudentEmail,photo")] Student student , HttpPostedFileBase photo)
{
if (ModelState.IsValid)
{
var fileName=Path.GetFileName(photo.FileName);
var path=Path.Combine(Server.MapPath("~/Images/") + fileName)
photo.SaveAs(path);
db.Students.Add(student);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(student);
}
I am trying to figure out why my model table's UserId column which references the primary key, "Id" of my AspNetUsers table (IdentityUser class in IdentityModel) is showing only NULL values when I 'Create()' an entry.
Here is the code for my two Create(), ActionResult methods in my controller class:
[Authorize]
public ActionResult Create()
{
ViewBag.UserId = new SelectList(db.Users, "Id", "Fullname");
return View();
}
// POST: Expenses/Create
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize]
public ActionResult Create([Bind(Include = "ID,Category,Description,GrossAmount,TaxAmount,NetAmount,Mileage,MileageRate,DateSubmitted,ExpenseDate,UserId")] Expense expense)
{
if (ModelState.IsValid)
{
expense.UserId = HttpContext.Current.User.Identity.Name;
db.Expenses.Add(expense);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.UserId = new SelectList(db.Users, "Id", "Fullname", expense.UserId);
return View(expense);
}
Here is the code for my Create view:
#{
ViewBag.Title = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
DateTime today = DateTime.Today;
string formattedDate = today.ToString("MM/dd/yyyy");
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Expense</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.UserId, htmlAttributes: new { #Value = user, #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.UserId, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.UserId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.DateSubmitted, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.DateSubmitted, new { htmlAttributes = new { #Value = formattedDate, #class = "form-control" } })
#Html.ValidationMessageFor(model => model.DateSubmitted, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ExpenseDate, htmlAttributes: new { #Value = #formattedDate, #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ExpenseDate, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ExpenseDate, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Category, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.Category, new SelectList(
new List<String>{
"Meals & Entertainment",
"Home Office",
"Travel",
"Phone",
"Auto - Mileage",
"Auto - Gas",
"Auto - Lease",
"Association Dues",
"Insurance Premium",
"Capital Assets",
"Trade Show & Promo",
"Pan Experience",
"Other"
}), new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.Category, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Description, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Description, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Description, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Mileage, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Mileage, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Mileage, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.MileageRate, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.MileageRate, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.MileageRate, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.GrossAmount, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.GrossAmount, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.GrossAmount, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.TaxAmount, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.TaxAmount, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.TaxAmount, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.NetAmount, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.NetAmount, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.NetAmount, "", 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 type="text/javascript">
var province = 'bc';
var grossAmount = document.getElementById('GrossAmount').value;
$(function () {
$('#ExpenseDate').datetimepicker({
defaultDate: '#formattedDate',
format: 'L',
showClose: true,
showClear: true,
toolbarPlacement: 'top'
});
});
$('#DateSubmitted').prop('readonly', true);
$('#Mileage').prop('disabled', true);
$('#MileageRate').prop('disabled', true);
$(function ()
{
$('#Category').on('change',function()
{
if ($(this).val() == 'Auto - Mileage')
{
$('#Mileage').prop('disabled', false);
$('#MileageRate').prop('disabled', false);
}
else
{
$('#Mileage').prop('disabled', true);
$('#MileageRate').prop('disabled', true);
}
}
)
})
</script>
}
If you would like to take a look at my model classes, you can go this post:
How To Restrict User Entries Only to that Sepcific User in EF 6/ASP.NET MVC 5
UPDATE:
Thanks to Steve Greene for putting me on the right track and for helping me to update my Create() method to this:
public ActionResult Create()
{
ViewBag.UserId = new SelectList(db.Users, "Id", "Fullname");
return View();
}
// POST: Expenses/Create
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize]
public ActionResult Create([Bind(Include = "ExpenseId,Category,Description,GrossAmount,TaxAmount,NetAmount,Mileage,MileageRate,DateSubmitted,ExpenseDate,UserId")] Expense expense)
{
if (ModelState.IsValid)
{
expense.UserId = System.Web.HttpContext.Current.User.Identity.Name;
db.Expenses.Add(expense);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.UserId = new SelectList(db.Users, "Id", "Fullname", expense.UserId);
foreach (ModelState modelState in ViewData.ModelState.Values)
{
foreach (ModelError error in modelState.Errors)
{
Response.Write(error);
}
}
Response.End();
return View(expense);
}
I don't get any major errors now, however, now ModelState.IsValid is returning False and the data I enter into Create.cshtml isn't being submitted.
I added a piece of code in the Create() method to catch the ModelState.Errors and it prints the error: System.Web.Mvc.ModelError.
I've also set a breakpoint before Create() and when I check the value of UserId, I get "null."
LAST UPDATE:
I've added a field for UserId to the Create.cshtml view and now I get the error: The INSERT statement conflicted with the FOREIGN KEY constraint "FK_dbo.Expenses_dbo.AspNetUsers_ApplicationUser_Id". The conflict occurred in database "PacificPetExpenses", table "dbo.AspNetUsers", column 'Id'.
To fix this, I modified something in my controller (please see my answer below).
Thank you.
If you are not using the UserId in the view, just set it before you add:
if (ModelState.IsValid)
{
expense.UserId = HttpContext.Current.User.Identity.Name; // or whatever
db.Expenses.Add(expense);
db.SaveChanges();
return RedirectToAction("Index");
}
Another common pattern is to add a hidden field for the UserId:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.HiddenFor(model => model.UserId) <-- now it will post back
...
If you want to have a drop down list of users, do this:
[Authorize]
public ActionResult Create()
{
ViewBag.UserList = new SelectList(db.Users, "Id", "Fullname");
return View();
}
Then in your view add a drop down list of users:
<div class="form-group">
#Html.LabelFor(model => model.UserId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.UserId, Model.UserList, new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.UserId, "", new { #class = "text-danger" })
</div>
</div>
Inside of the Create() method inside of my controller, I had the line:
expense.UserId = HttpContext.Current.User.Identity.Name;
I set up a break point and as breakpoint was on the line, it showed the correct UserId; however, after the line had executed, it showed the user's email address.
To fix this I tried changing the line above to:
expense.UserId = System.Web.HttpContext.Current.User.Identity.GetUserId();
and expenses were both being submitted with no errors and displaying in the view where it was supposed to show the list of expenses submitted by the user!